From 0902c7480984f194683e306255c9fad4eb4b0f3f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 3 Aug 2020 10:01:52 +0200 Subject: [PATCH 01/91] [hostinfo] Editorialize on the tests - the implementation understands Intel and AMD, but the test was written for my desktop machine (which fails elsewhere). SEE #1471 --- src/modules/hostinfo/Tests.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/hostinfo/Tests.cpp b/src/modules/hostinfo/Tests.cpp index 8241ef022..8182da365 100644 --- a/src/modules/hostinfo/Tests.cpp +++ b/src/modules/hostinfo/Tests.cpp @@ -59,7 +59,12 @@ HostInfoTests::testHostOS() QCOMPARE( expect, hostOS() ); QCOMPARE( expect, hostOSName() ); // Might be the same - QCOMPARE( QStringLiteral( "Intel" ), hostCPU() ); // On all my developer machines + + // This is a lousy test, too: the implementation reads /proc/cpuinfo + // and that's the only way we could use, too, to find what the "right" + // answer is. + QStringList cpunames{ QStringLiteral( "Intel" ), QStringLiteral( "AMD" ) }; + QVERIFY( cpunames.contains( hostCPU() ) ); } From 29cfcb01dabcd33d12654375892697c39071aaa5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 12:37:46 +0200 Subject: [PATCH 02/91] i18n: suppress Interlingue - like Esperanto before Qt 5.12, Interlingue does not seem to be supported by QLocale, so it gets turned into "C" locale, which then messes up the default language selection in the welcome page. Move it to _incomplete until QLocale does support it. FIXES #1475 --- CMakeLists.txt | 53 ++++++++++++++++---------------------------------- ci/txstats.py | 1 + 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cabd72700..8fcebae6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,20 +140,18 @@ set( CALAMARES_DESCRIPTION_SUMMARY # TODO: drop the es_ES translation from Transifex # # NOTE: move eo (Esperanto) to _ok once Qt can actually create a -# locale for it. (Qt 5.12.2 can, see check later on). -# NOTE: update these lines by running txstats.py, or copy these four lines -# and prefix each variable name with "p", so that the automatic -# checks for new languages and misspelled ones are done (that is, -# copy these four lines to four backup lines, add "p", and then update -# the original four lines with the current translations). +# locale for it. (Qt 5.12.2 can, see Translation Status section). +# NOTE: move ie (Interlingue) to _ok once Qt supports it. +# NOTE: update these lines by running `txstats.py`, or for full automation +# `txstats.py -e`. See also # # Total 68 languages set( _tx_complete ca fi_FI he hr nl pt_BR sq tg tr_TR uk zh_TW ) set( _tx_good as ast az az_AZ be cs_CZ da de es fr hi hu it_IT ja ko lt ml pt_PT ru sk sv zh_CN ) -set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id ie is mr nb +set( _tx_ok ar bg el en_GB es_MX es_PR et eu fa gl id is mr nb pl ro sl sr sr@latin th ) -set( _tx_incomplete bn ca@valencia eo fr_CH gu kk kn lo lv mk ne_NP +set( _tx_incomplete bn ca@valencia eo fr_CH gu ie kk kn lo lv mk ne_NP ur uz ) ### Required versions @@ -286,14 +284,6 @@ find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Concurrent Core Gui LinguistTool if( WITH_QML ) find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Quick QuickWidgets ) endif() -if( Qt5_VERSION VERSION_GREATER 5.12.1 ) - # At least Qt 5.12.2 seems to support Esperanto in QLocale - if( "eo" IN_LIST _tx_incomplete ) - message(STATUS "Esperanto support since Qt 5.12.2, enabling Esperanto locale") - list( REMOVE_ITEM _tx_incomplete "eo" ) - list( APPEND _tx_ok "eo" ) - endif() -endif() # Optional Qt parts find_package( Qt5DBus CONFIG ) @@ -429,30 +419,22 @@ set(Calamares_WITH_QML ${WITH_QML}) ### Transifex Translation status # -# Construct language lists for use. If there are p_tx* variables, -# then run an extra cmake-time check for consistency of the old -# (p_tx*) and new (_tx*) lists. +# Construct language lists for use. # -set( prev_tx ${p_tx_complete} ${p_tx_good} ${p_tx_ok} ${p_tx_incomplete} ) +if( Qt5_VERSION VERSION_GREATER 5.12.1 ) + # At least Qt 5.12.2 seems to support Esperanto in QLocale + if( "eo" IN_LIST _tx_incomplete ) + message(STATUS "Esperanto support since Qt 5.12.2, enabling Esperanto locale") + list( REMOVE_ITEM _tx_incomplete "eo" ) + list( APPEND _tx_ok "eo" ) + endif() +endif() + set( curr_tx ${_tx_complete} ${_tx_good} ${_tx_ok} ${_tx_incomplete} ) set( tx_errors OFF ) -if ( prev_tx ) - # Gone in new list - foreach( l ${prev_tx} ) - list( FIND curr_tx ${l} p_l ) - if( p_l EQUAL -1 ) - message(WARNING "Language ${l} was present in previous translations and is now absent.") - set( tx_errors ON ) - endif() - endforeach() - +if ( curr_tx ) # New in list foreach( l ${curr_tx} ) - list( FIND prev_tx ${l} p_l ) - if( p_l EQUAL -1 ) - message(WARNING "Language ${l} is new.") - set( tx_errors ON ) - endif() set( p_l "lang/calamares_${l}.ts" ) if( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${p_l} ) message(WARNING "Language ${l} has no .ts file yet.") @@ -463,7 +445,6 @@ if ( prev_tx ) unset( p_l ) unset( l ) endif() -unset( prev_tx ) unset( curr_tx ) if( tx_errors ) message( FATAL_ERROR "Translation warnings, see above." ) diff --git a/ci/txstats.py b/ci/txstats.py index a9f412743..40fe3f43b 100755 --- a/ci/txstats.py +++ b/ci/txstats.py @@ -178,6 +178,7 @@ def get_tx_stats(languages, outputter, verbose): # and it's at-the-least ok. incomplete_languages = ( "eo", # Not supported by QLocale < 5.12.1 + "ie", # Not supported by Qt at least through 5.15.0 ) all_langs = [] From b27bc11975339915b60e973b8081bdbf7cc7feac Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 12:59:55 +0200 Subject: [PATCH 03/91] [libcalamares] Merge locale tests files - No need for a separate .h in most test cases --- src/libcalamares/locale/Tests.cpp | 35 +++++++++++++++++----- src/libcalamares/locale/Tests.h | 49 ------------------------------- 2 files changed, 28 insertions(+), 56 deletions(-) delete mode 100644 src/libcalamares/locale/Tests.h diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 6c5a508d7..3ee3dfdd3 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,13 +16,8 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ -#include "Tests.h" - #include "locale/LabelModel.h" #include "locale/TimeZone.h" #include "locale/TranslatableConfiguration.h" @@ -31,7 +27,26 @@ #include -QTEST_GUILESS_MAIN( LocaleTests ) +class LocaleTests : public QObject +{ + Q_OBJECT +public: + LocaleTests(); + ~LocaleTests() override; + +private Q_SLOTS: + void initTestCase(); + + void testLanguageModelCount(); + void testEsperanto(); + void testTranslatableLanguages(); + void testTranslatableConfig1(); + void testTranslatableConfig2(); + + // TimeZone testing + void testSimpleZones(); + void testComplexZones(); +}; LocaleTests::LocaleTests() {} @@ -257,3 +272,9 @@ LocaleTests::testComplexZones() QCOMPARE( r.tr(), QStringLiteral( "zxc,;* vm" ) ); // Only _ is special } } + +QTEST_GUILESS_MAIN( LocaleTests ) + +#include "utils/moc-warnings.h" + +#include "Tests.moc" diff --git a/src/libcalamares/locale/Tests.h b/src/libcalamares/locale/Tests.h deleted file mode 100644 index dfe85a865..000000000 --- a/src/libcalamares/locale/Tests.h +++ /dev/null @@ -1,49 +0,0 @@ -/* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2019 Adriaan de Groot - * - * Calamares is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Calamares is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Calamares. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * - */ - -#ifndef LIBCALAMARES_LOCALE_TESTS_H -#define LIBCALAMARES_LOCALE_TESTS_H - -#include - -class LocaleTests : public QObject -{ - Q_OBJECT -public: - LocaleTests(); - ~LocaleTests() override; - -private Q_SLOTS: - void initTestCase(); - - void testLanguageModelCount(); - void testEsperanto(); - void testTranslatableLanguages(); - void testTranslatableConfig1(); - void testTranslatableConfig2(); - - // TimeZone testing - void testSimpleZones(); - void testComplexZones(); -}; - -#endif From 3f1b31e352cb6fba908f0d1e5c1b413217951972 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 13:21:08 +0200 Subject: [PATCH 04/91] [libcalamares] Explicit tests for Interlingue - The language code "ie" is not recognized, - "ia" is, and it seems to be the post-war variant of Interlingue, so we may want to rename / relabel. The testEsperanto test -- now split into scripts and esperanto -- would have picked "ie" out of the list because it does map to C locale. --- src/libcalamares/locale/Tests.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 3ee3dfdd3..10b4ad056 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -38,10 +38,13 @@ private Q_SLOTS: void initTestCase(); void testLanguageModelCount(); - void testEsperanto(); void testTranslatableLanguages(); void testTranslatableConfig1(); void testTranslatableConfig2(); + void testLanguageScripts(); + + void testEsperanto(); + void testInterlingue(); // TimeZone testing void testSimpleZones(); @@ -55,6 +58,8 @@ LocaleTests::~LocaleTests() {} void LocaleTests::initTestCase() { + Logger::setupLogLevel( Logger::LOGDEBUG ); + // Otherwise plain get() is dubious in the TranslatableConfiguration tests QLocale::setDefault( QLocale( QStringLiteral( "en_US" ) ) ); QVERIFY( ( QLocale().name() == "C" ) || ( QLocale().name() == "en_US" ) ); @@ -80,10 +85,8 @@ LocaleTests::testLanguageModelCount() } void -LocaleTests::testEsperanto() +LocaleTests::testLanguageScripts() { - Logger::setupLogLevel( Logger::LOGDEBUG ); - const auto* m = CalamaresUtils::Locale::availableTranslations(); QVERIFY( m ); @@ -104,13 +107,33 @@ LocaleTests::testEsperanto() QVERIFY( locale.language() == QLocale::Lithuanian ? locale.country() == QLocale::Lithuania : true ); QVERIFY( locale.language() != QLocale::C ); } +} + +void +LocaleTests::testEsperanto() +{ #if QT_VERSION < QT_VERSION_CHECK( 5, 12, 2 ) QCOMPARE( QLocale( "eo" ).language(), QLocale::C ); #else QCOMPARE( QLocale( "eo" ).language(), QLocale::Esperanto ); #endif + QCOMPARE( QLocale( QLocale::Esperanto ).language(), QLocale::Esperanto ); // Probably fails on 5.12, too } +void +LocaleTests::testInterlingue() +{ + // ie / Interlingue is borked (is "ie" even the right name?) + QCOMPARE( QLocale( "ie" ).language(), QLocale::C ); + QCOMPARE( QLocale( QLocale::Interlingue ).language(), QLocale::English ); + + // "ia" exists (post-war variant of Interlingue) + QCOMPARE( QLocale( "ia" ).language(), QLocale::Interlingua ); + // "bork" does not exist + QCOMPARE( QLocale( "bork" ).language(), QLocale::C ); +} + + static const QStringList& someLanguages() { From 05f3fbea05629a3fef0316cb3b98625f88d52866 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 13:45:36 +0200 Subject: [PATCH 05/91] [locale] Apply SPDX headers --- src/modules/locale/LocalePage.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/locale/LocalePage.cpp b/src/modules/locale/LocalePage.cpp index 406f27a6e..c10b2dee9 100644 --- a/src/modules/locale/LocalePage.cpp +++ b/src/modules/locale/LocalePage.cpp @@ -1,7 +1,8 @@ /* === This file is part of Calamares - === * - * Copyright 2014-2016, Teo Mrnjavac - * Copyright 2017-2019, Adriaan de Groot + * SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 44bc61d4be71a3d9bd965378d0a22d9725d75420 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 22:35:04 +0200 Subject: [PATCH 06/91] [users] set up Config object before widget --- src/modules/users/UsersViewStep.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/users/UsersViewStep.cpp b/src/modules/users/UsersViewStep.cpp index b9ea3ae17..39d06b64a 100644 --- a/src/modules/users/UsersViewStep.cpp +++ b/src/modules/users/UsersViewStep.cpp @@ -168,6 +168,8 @@ UsersViewStep::onLeave() void UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { + m_config->setConfigurationMap( configurationMap ); + // Create the widget, after all .. as long as writing configuration to the UI is needed (void)this->widget(); using CalamaresUtils::getBool; @@ -204,6 +206,4 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap ) Action hostsfileAction = getBool( configurationMap, "writeHostsFile", true ) ? Action::WriteEtcHosts : Action::None; m_actions = hostsfileAction | hostnameAction; - - m_config->setConfigurationMap( configurationMap ); } From 35dff4d12c0aa39324c15761541b74e129004b4d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 22:16:48 +0200 Subject: [PATCH 07/91] [users] Migrate reuse-password and password-strength to Config - add the "reuse user password for root" setting to Config, make the UI page follow that setting. - add the require-strong-password default and toggle settings to Config; this is not well-checked yet. On the widget / UI side, connect checkboxes only if they are visible; refactor reuse-user-password-for-root settings. --- src/modules/users/Config.cpp | 27 ++++++++++ src/modules/users/Config.h | 26 ++++++++++ src/modules/users/UsersPage.cpp | 77 +++++++++++++++-------------- src/modules/users/UsersPage.h | 5 +- src/modules/users/UsersViewStep.cpp | 5 -- 5 files changed, 95 insertions(+), 45 deletions(-) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index e0174e74e..823b9fb99 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -343,6 +343,27 @@ Config::setAutoLogin( bool b ) } } +void +Config::setReuseUserPasswordForRoot( bool reuse ) +{ + if ( reuse != m_reuseUserPasswordForRoot ) + { + m_reuseUserPasswordForRoot = reuse; + emit reuseUserPasswordForRootChanged( reuse ); + } +} + +void +Config::setRequireStrongPasswords( bool strong ) +{ + if ( strong != m_requireStrongPasswords ) + { + m_requireStrongPasswords = strong; + emit requireStrongPasswordsChanged( strong ); + } +} + + STATICTEST void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ) { @@ -377,4 +398,10 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) m_writeRootPassword = CalamaresUtils::getBool( configurationMap, "setRootPassword", true ); Calamares::JobQueue::instance()->globalStorage()->insert( "setRootPassword", m_writeRootPassword ); + + m_reuseUserPasswordForRoot = CalamaresUtils::getBool( configurationMap, "doReusePassword", false ); + + m_permitWeakPasswords = CalamaresUtils::getBool( configurationMap, "allowWeakPasswords", false ); + m_requireStrongPasswords + = !m_permitWeakPasswords || !CalamaresUtils::getBool( configurationMap, "allowWeakPasswordsDefault", false ); } diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h index b84dc5aaf..8c30dad4c 100644 --- a/src/modules/users/Config.h +++ b/src/modules/users/Config.h @@ -42,6 +42,14 @@ class Config : public QObject Q_PROPERTY( QString hostName READ hostName WRITE setHostName NOTIFY hostNameChanged ) Q_PROPERTY( QString hostNameStatus READ hostNameStatus NOTIFY hostNameStatusChanged ) + Q_PROPERTY( bool writeRootPassword READ writeRootPassword CONSTANT ) + Q_PROPERTY( bool reuseUserPasswordForRoot READ reuseUserPasswordForRoot WRITE setReuseUserPasswordForRoot NOTIFY + reuseUserPasswordForRootChanged ) + + Q_PROPERTY( bool permitWeakPasswords READ permitWeakPasswords CONSTANT ) + Q_PROPERTY( bool requireStrongPasswords READ requireStrongPasswords WRITE setRequireStrongPasswords NOTIFY + requireStrongPasswordsChanged ) + public: Config( QObject* parent = nullptr ); ~Config(); @@ -76,6 +84,12 @@ public: bool doAutoLogin() const { return m_doAutoLogin; } /// Should the root password be written (if false, no password is set and the root account is disabled for login) bool writeRootPassword() const { return m_writeRootPassword; } + /// Should the user's password be used for root, too? (if root is written at all) + bool reuseUserPasswordForRoot() const { return m_reuseUserPasswordForRoot; } + /// Show UI to change the "require strong password" setting? + bool permitWeakPasswords() const { return m_permitWeakPasswords; } + /// Current setting for "require strong password"? + bool requireStrongPasswords() const { return m_requireStrongPasswords; } const QStringList& defaultGroups() const { return m_defaultGroups; } @@ -109,6 +123,11 @@ public Q_SLOTS: /// Sets the autologin flag void setAutoLogin( bool b ); + /// Set to true to use the user password, unchanged, for root too + void setReuseUserPasswordForRoot( bool reuse ); + /// Change setting for "require strong password" + void setRequireStrongPasswords( bool strong ); + signals: void userShellChanged( const QString& ); void autologinGroupChanged( const QString& ); @@ -119,6 +138,8 @@ signals: void hostNameChanged( const QString& ); void hostNameStatusChanged( const QString& ); void autoLoginChanged( bool ); + void reuseUserPasswordForRootChanged( bool ); + void requireStrongPasswordsChanged( bool ); private: QStringList m_defaultGroups; @@ -129,7 +150,12 @@ private: QString m_loginName; QString m_hostName; bool m_doAutoLogin = false; + bool m_writeRootPassword = true; + bool m_reuseUserPasswordForRoot = false; + + bool m_permitWeakPasswords = false; + bool m_requireStrongPasswords = true; bool m_customLoginName = false; bool m_customHostName = false; diff --git a/src/modules/users/UsersPage.cpp b/src/modules/users/UsersPage.cpp index c4502256a..9054d60e7 100644 --- a/src/modules/users/UsersPage.cpp +++ b/src/modules/users/UsersPage.cpp @@ -105,6 +105,12 @@ UsersPage::UsersPage( Config* config, QWidget* parent ) { ui->setupUi( this ); + ui->checkBoxReusePassword->setVisible( m_config->writeRootPassword() ); + ui->checkBoxReusePassword->setChecked( m_config->reuseUserPasswordForRoot() ); + + ui->checkBoxValidatePassword->setVisible( m_config->permitWeakPasswords() ); + ui->checkBoxValidatePassword->setChecked( m_config->requireStrongPasswords() ); + // Connect signals and slots connect( ui->textBoxUserPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged ); connect( ui->textBoxUserVerifiedPassword, &QLineEdit::textChanged, this, &UsersPage::onPasswordTextChanged ); @@ -115,21 +121,7 @@ UsersPage::UsersPage( Config* config, QWidget* parent ) onRootPasswordTextChanged( ui->textBoxRootPassword->text() ); checkReady( isReady() ); } ); - connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, [this]( const int checked ) { - /* When "reuse" is checked, hide the fields for explicitly - * entering the root password. However, if we're going to - * disable the root password anyway, hide them all regardless of - * the checkbox -- so when writeRoot is false, checked needs - * to be true, to hide them all. - */ - const bool visible = m_config->writeRootPassword() ? !checked : false; - ui->labelChooseRootPassword->setVisible( visible ); - ui->labelRootPassword->setVisible( visible ); - ui->labelRootPasswordError->setVisible( visible ); - ui->textBoxRootPassword->setVisible( visible ); - ui->textBoxVerifiedRootPassword->setVisible( visible ); - checkReady( isReady() ); - } ); + connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, &UsersPage::onReuseUserPasswordChanged ); connect( ui->textBoxFullName, &QLineEdit::textEdited, config, &Config::setFullName ); connect( config, &Config::fullNameChanged, this, &UsersPage::onFullNameTextEdited ); @@ -147,13 +139,25 @@ UsersPage::UsersPage( Config* config, QWidget* parent ) } ); connect( config, &Config::autoLoginChanged, ui->checkBoxDoAutoLogin, &QCheckBox::setChecked ); - ui->checkBoxReusePassword->setVisible( m_config->writeRootPassword() ); - ui->checkBoxReusePassword->setChecked( true ); - ui->checkBoxValidatePassword->setChecked( true ); + if ( m_config->writeRootPassword() ) + { + connect( ui->checkBoxReusePassword, &QCheckBox::stateChanged, this, [this]( int checked ) { + m_config->setReuseUserPasswordForRoot( checked != Qt::Unchecked ); + } ); + connect( config, &Config::reuseUserPasswordForRootChanged, ui->checkBoxReusePassword, &QCheckBox::setChecked ); + } - setPasswordCheckboxVisible( false ); + if ( m_config->permitWeakPasswords() ) + { + connect( ui->checkBoxValidatePassword, &QCheckBox::stateChanged, this, [this]( int checked ) { + m_config->setRequireStrongPasswords( checked != Qt::Unchecked ); + } ); + connect( config, &Config::requireStrongPasswordsChanged, ui->checkBoxValidatePassword, &QCheckBox::setChecked ); + } CALAMARES_RETRANSLATE_SLOT( &UsersPage::retranslate ); + + onReuseUserPasswordChanged( m_config->reuseUserPasswordForRoot() ); } UsersPage::~UsersPage() @@ -335,25 +339,26 @@ UsersPage::onRootPasswordTextChanged( const QString& ) emit checkReady( isReady() ); } - void -UsersPage::setPasswordCheckboxVisible( bool visible ) +UsersPage::onReuseUserPasswordChanged( const int checked ) { - ui->checkBoxValidatePassword->setVisible( visible ); -} - -void -UsersPage::setValidatePasswordDefault( bool checked ) -{ - ui->checkBoxValidatePassword->setChecked( checked ); - emit checkReady( isReady() ); -} - -void -UsersPage::setReusePasswordDefault( bool checked ) -{ - ui->checkBoxReusePassword->setChecked( checked ); - emit checkReady( isReady() ); + /* When "reuse" is checked, hide the fields for explicitly + * entering the root password. However, if we're going to + * disable the root password anyway, hide them all regardless of + * the checkbox -- so when writeRoot is false, visible needs + * to be false, to hide them all. + * + * In principle this is only connected when writeRootPassword is @c true, + * but it is **always** called at least once in the constructor + * to set up initial visibility. + */ + const bool visible = m_config->writeRootPassword() ? !checked : false; + ui->labelChooseRootPassword->setVisible( visible ); + ui->labelRootPassword->setVisible( visible ); + ui->labelRootPasswordError->setVisible( visible ); + ui->textBoxRootPassword->setVisible( visible ); + ui->textBoxVerifiedRootPassword->setVisible( visible ); + checkReady( isReady() ); } void diff --git a/src/modules/users/UsersPage.h b/src/modules/users/UsersPage.h index b8cb0f06a..5e0909b54 100644 --- a/src/modules/users/UsersPage.h +++ b/src/modules/users/UsersPage.h @@ -50,10 +50,6 @@ public: void onActivate(); - void setPasswordCheckboxVisible( bool visible ); - void setValidatePasswordDefault( bool checked ); - void setReusePasswordDefault( bool checked ); - /** @brief Process entries in the passwordRequirements config entry * * Called once for each item in the config entry, which should @@ -73,6 +69,7 @@ protected slots: void reportHostNameStatus( const QString& ); void onPasswordTextChanged( const QString& ); void onRootPasswordTextChanged( const QString& ); + void onReuseUserPasswordChanged( const int ); signals: void checkReady( bool ); diff --git a/src/modules/users/UsersViewStep.cpp b/src/modules/users/UsersViewStep.cpp index 39d06b64a..092826d24 100644 --- a/src/modules/users/UsersViewStep.cpp +++ b/src/modules/users/UsersViewStep.cpp @@ -174,8 +174,6 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap ) (void)this->widget(); using CalamaresUtils::getBool; - m_widget->setReusePasswordDefault( getBool( configurationMap, "doReusePassword", false ) ); - if ( configurationMap.contains( "passwordRequirements" ) && configurationMap.value( "passwordRequirements" ).type() == QVariant::Map ) { @@ -187,9 +185,6 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap ) } } - m_widget->setPasswordCheckboxVisible( getBool( configurationMap, "allowWeakPasswords", false ) ); - m_widget->setValidatePasswordDefault( !getBool( configurationMap, "allowWeakPasswordsDefault", false ) ); - using Action = SetHostNameJob::Action; QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" ); From a8c4f5b758ac17960a5b79d1653c0649a23627c8 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 4 Aug 2020 23:09:51 +0200 Subject: [PATCH 08/91] CMake: fix up warnings from the LibPWQuality module - don't include other find modules - pkgconfig isn't totally necessary, it might work without --- CMakeModules/FindLibPWQuality.cmake | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeModules/FindLibPWQuality.cmake b/CMakeModules/FindLibPWQuality.cmake index 84136f5ad..2728afb1e 100644 --- a/CMakeModules/FindLibPWQuality.cmake +++ b/CMakeModules/FindLibPWQuality.cmake @@ -6,10 +6,16 @@ # LibPWQuality_LIBRARIES, where to find the library # LibPWQuality_INCLUDE_DIRS, where to find pwquality.h # -include(FindPkgConfig) +find_package(PkgConfig) include(FindPackageHandleStandardArgs) -pkg_search_module(pc_pwquality QUIET pwquality) +if(PkgConfig_FOUND) + pkg_search_module(pc_pwquality QUIET pwquality) +else() + # It's just possible that the find_path and find_library will + # find it **anyway**, so let's pretend it was there. + set(pc_pwquality_FOUND ON) +endif() find_path(LibPWQuality_INCLUDE_DIR NAMES pwquality.h From f7102527a8d26183d49eb4a375d6c133ba833557 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 00:51:08 +0200 Subject: [PATCH 09/91] CMake: stop clobbering config files When CMake runs, configure_file() will clobber the config files in the build/ directory, which is annoying during testing: you need to keep making the same edits, or edit the source. - Introduce new behavior: the config file is **not** overwritten unless the source file is newer. This means that edits to config files in the build directory are preserved. - If INSTALL_CONFIG is **on** then the files are clobbered anyway (the source is considered new regardless). --- CMakeModules/CalamaresAddPlugin.cmake | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeModules/CalamaresAddPlugin.cmake b/CMakeModules/CalamaresAddPlugin.cmake index 66536eda7..ee3c63acb 100644 --- a/CMakeModules/CalamaresAddPlugin.cmake +++ b/CMakeModules/CalamaresAddPlugin.cmake @@ -187,13 +187,22 @@ function( calamares_add_plugin ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_DESC_FILE} DESTINATION ${PLUGIN_DESTINATION} ) + set( _warned_config OFF ) foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} ) - configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} OR INSTALL_CONFIG ) + configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + else() + message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" ) + set( _warned_config ON ) + endif() if ( INSTALL_CONFIG ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} DESTINATION ${PLUGIN_DATA_DESTINATION} ) endif() endforeach() + if ( _warned_config ) + message( "" ) + endif() endif() endfunction() From 32e3933355e501a75bdc8229503b98f4ecdec2c4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 00:51:08 +0200 Subject: [PATCH 10/91] CMake: stop clobbering config files When CMake runs, configure_file() will clobber the config files in the build/ directory, which is annoying during testing: you need to keep making the same edits, or edit the source. - Introduce new behavior: the config file is **not** overwritten unless the source file is newer. This means that edits to config files in the build directory are preserved. - If INSTALL_CONFIG is **on** then the files are clobbered anyway (the source is considered new regardless). --- CMakeModules/CalamaresAddPlugin.cmake | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeModules/CalamaresAddPlugin.cmake b/CMakeModules/CalamaresAddPlugin.cmake index 66536eda7..ee3c63acb 100644 --- a/CMakeModules/CalamaresAddPlugin.cmake +++ b/CMakeModules/CalamaresAddPlugin.cmake @@ -187,13 +187,22 @@ function( calamares_add_plugin ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_DESC_FILE} DESTINATION ${PLUGIN_DESTINATION} ) + set( _warned_config OFF ) foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} ) - configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} OR INSTALL_CONFIG ) + configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + else() + message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" ) + set( _warned_config ON ) + endif() if ( INSTALL_CONFIG ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} DESTINATION ${PLUGIN_DATA_DESTINATION} ) endif() endforeach() + if ( _warned_config ) + message( "" ) + endif() endif() endfunction() From 2efce1ac7a0f4ef05405fe1373db86cd9213e5ac Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 10:29:13 +0200 Subject: [PATCH 11/91] [users] Move the hostname-setting config - The configuration for writing the hostname (to /etc/hostname, to /etc/hosts and possibly to systemd-hostname) is read-only, because it comes from the config file and won't change after. --- src/modules/users/Config.cpp | 37 ++++++++++++++++++++++++++ src/modules/users/Config.h | 19 ++++++++++++++ src/modules/users/SetHostNameJob.cpp | 14 +++++----- src/modules/users/SetHostNameJob.h | 18 +++---------- src/modules/users/UsersViewStep.cpp | 39 ++-------------------------- src/modules/users/UsersViewStep.h | 4 --- 6 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index 823b9fb99..b043691f8 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -36,6 +36,22 @@ static const QRegExp HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" ); static constexpr const int HOSTNAME_MIN_LENGTH = 2; static constexpr const int HOSTNAME_MAX_LENGTH = 63; +const NamedEnumTable< HostNameAction >& +hostNameActionNames() +{ + // *INDENT-OFF* + // clang-format off + static const NamedEnumTable< HostNameAction > names { + { QStringLiteral( "none" ), HostNameAction::None }, + { QStringLiteral( "etcfile" ), HostNameAction::EtcHostname }, + { QStringLiteral( "hostnamed" ), HostNameAction::SystemdHostname } + }; + // clang-format on + // *INDENT-ON* + + return names; +} + Config::Config( QObject* parent ) : QObject( parent ) { @@ -378,6 +394,25 @@ setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroup } } +STATICTEST HostNameActions +getHostNameActions( const QVariantMap& configurationMap ) +{ + HostNameAction setHostName = HostNameAction::EtcHostname; + QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" ); + if ( !hostnameActionString.isEmpty() ) + { + bool ok = false; + setHostName = hostNameActionNames().find( hostnameActionString, ok ); + if ( !ok ) + { + setHostName = HostNameAction::EtcHostname; // Rather than none + } + } + + HostNameAction writeHosts = CalamaresUtils::getBool( configurationMap, "writeHostsFile", true ) ? HostNameAction::WriteEtcHosts : HostNameAction::None; + return setHostName | writeHosts; +} + void Config::setConfigurationMap( const QVariantMap& configurationMap ) @@ -393,6 +428,8 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) setAutologinGroup( CalamaresUtils::getString( configurationMap, "autologinGroup" ) ); setSudoersGroup( CalamaresUtils::getString( configurationMap, "sudoersGroup" ) ); + m_hostNameActions = getHostNameActions( configurationMap ); + setConfigurationDefaultGroups( configurationMap, m_defaultGroups ); m_doAutoLogin = CalamaresUtils::getBool( configurationMap, "doAutologin", false ); diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h index 8c30dad4c..e61564897 100644 --- a/src/modules/users/Config.h +++ b/src/modules/users/Config.h @@ -21,9 +21,23 @@ #ifndef USERS_CONFIG_H #define USERS_CONFIG_H +#include "utils/NamedEnum.h" + #include #include +enum HostNameAction +{ + None = 0x0, + EtcHostname = 0x1, // Write to /etc/hostname directly + SystemdHostname = 0x2, // Set via hostnamed(1) + WriteEtcHosts = 0x4 // Write /etc/hosts (127.0.1.1 is this host) +}; +Q_DECLARE_FLAGS( HostNameActions, HostNameAction ) +Q_DECLARE_OPERATORS_FOR_FLAGS( HostNameActions ) + +const NamedEnumTable< HostNameAction >& hostNameActionNames(); + class Config : public QObject { Q_OBJECT @@ -41,6 +55,7 @@ class Config : public QObject Q_PROPERTY( QString hostName READ hostName WRITE setHostName NOTIFY hostNameChanged ) Q_PROPERTY( QString hostNameStatus READ hostNameStatus NOTIFY hostNameStatusChanged ) + Q_PROPERTY( HostNameActions hostNameActions READ hostNameActions CONSTANT ) Q_PROPERTY( bool writeRootPassword READ writeRootPassword CONSTANT ) Q_PROPERTY( bool reuseUserPasswordForRoot READ reuseUserPasswordForRoot WRITE setReuseUserPasswordForRoot NOTIFY @@ -79,6 +94,8 @@ public: QString hostName() const { return m_hostName; } /// Status message about hostname -- empty for "ok" QString hostNameStatus() const; + /// How to write the hostname + HostNameActions hostNameActions() const { return m_hostNameActions; } /// Should the user be automatically logged-in? bool doAutoLogin() const { return m_doAutoLogin; } @@ -159,6 +176,8 @@ private: bool m_customLoginName = false; bool m_customHostName = false; + + HostNameActions m_hostNameActions; }; #endif diff --git a/src/modules/users/SetHostNameJob.cpp b/src/modules/users/SetHostNameJob.cpp index 555fcdc7d..a4cfa0268 100644 --- a/src/modules/users/SetHostNameJob.cpp +++ b/src/modules/users/SetHostNameJob.cpp @@ -27,13 +27,13 @@ #include #include -#include -#include -#include +#include +#include +#include using WriteMode = CalamaresUtils::System::WriteMode; -SetHostNameJob::SetHostNameJob( const QString& hostname, Actions a ) +SetHostNameJob::SetHostNameJob( const QString& hostname, HostNameActions a ) : Calamares::Job() , m_hostname( hostname ) , m_actions( a ) @@ -138,7 +138,7 @@ SetHostNameJob::exec() return Calamares::JobResult::error( tr( "Internal Error" ) ); } - if ( m_actions & Action::EtcHostname ) + if ( m_actions & HostNameAction::EtcHostname ) { if ( !setFileHostname( m_hostname ) ) { @@ -147,7 +147,7 @@ SetHostNameJob::exec() } } - if ( m_actions & Action::WriteEtcHosts ) + if ( m_actions & HostNameAction::WriteEtcHosts ) { if ( !writeFileEtcHosts( m_hostname ) ) { @@ -156,7 +156,7 @@ SetHostNameJob::exec() } } - if ( m_actions & Action::SystemdHostname ) + if ( m_actions & HostNameAction::SystemdHostname ) { // Does its own logging setSystemdHostname( m_hostname ); diff --git a/src/modules/users/SetHostNameJob.h b/src/modules/users/SetHostNameJob.h index e3867b9d0..5d52b545b 100644 --- a/src/modules/users/SetHostNameJob.h +++ b/src/modules/users/SetHostNameJob.h @@ -21,23 +21,15 @@ #ifndef SETHOSTNAMEJOB_CPP_H #define SETHOSTNAMEJOB_CPP_H +#include "Config.h" + #include "Job.h" class SetHostNameJob : public Calamares::Job { Q_OBJECT public: - enum Action - { - None = 0x0, - EtcHostname = 0x1, // Write to /etc/hostname directly - SystemdHostname = 0x2, // Set via hostnamed(1) - WriteEtcHosts = 0x4 // Write /etc/hosts (127.0.1.1 is this host) - }; - Q_DECLARE_FLAGS( Actions, Action ) - - - SetHostNameJob( const QString& hostname, Actions a ); + SetHostNameJob( const QString& hostname, HostNameActions a ); QString prettyName() const override; QString prettyDescription() const override; QString prettyStatusMessage() const override; @@ -45,9 +37,7 @@ public: private: const QString m_hostname; - const Actions m_actions; + const HostNameActions m_actions; }; -Q_DECLARE_OPERATORS_FOR_FLAGS( SetHostNameJob::Actions ) - #endif // SETHOSTNAMEJOB_CPP_H diff --git a/src/modules/users/UsersViewStep.cpp b/src/modules/users/UsersViewStep.cpp index 092826d24..300bdf072 100644 --- a/src/modules/users/UsersViewStep.cpp +++ b/src/modules/users/UsersViewStep.cpp @@ -34,28 +34,9 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( UsersViewStepFactory, registerPlugin< UsersViewStep >(); ) -static const NamedEnumTable< SetHostNameJob::Action >& -hostnameActions() -{ - using Action = SetHostNameJob::Action; - - // *INDENT-OFF* - // clang-format off - static const NamedEnumTable< Action > names { - { QStringLiteral( "none" ), Action::None }, - { QStringLiteral( "etcfile" ), Action::EtcHostname }, - { QStringLiteral( "hostnamed" ), Action::SystemdHostname } - }; - // clang-format on - // *INDENT-ON* - - return names; -} - UsersViewStep::UsersViewStep( QObject* parent ) : Calamares::ViewStep( parent ) , m_widget( nullptr ) - , m_actions( SetHostNameJob::Action::None ) , m_config( new Config( this ) ) { emit nextStatusChanged( true ); @@ -158,7 +139,8 @@ UsersViewStep::onLeave() j = new SetPasswordJob( "root", m_widget->getRootPassword() ); m_jobs.append( Calamares::job_ptr( j ) ); - j = new SetHostNameJob( m_config->hostName(), m_actions ); + // TODO: Config object should create jobs + j = new SetHostNameJob( m_config->hostName(), m_config->hostNameActions() ); m_jobs.append( Calamares::job_ptr( j ) ); m_widget->fillGlobalStorage(); @@ -184,21 +166,4 @@ UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap ) m_widget->addPasswordCheck( i.key(), i.value() ); } } - - using Action = SetHostNameJob::Action; - - QString hostnameActionString = CalamaresUtils::getString( configurationMap, "setHostname" ); - if ( hostnameActionString.isEmpty() ) - { - hostnameActionString = QStringLiteral( "EtcFile" ); - } - bool ok = false; - auto hostnameAction = hostnameActions().find( hostnameActionString, ok ); - if ( !ok ) - { - hostnameAction = Action::EtcHostname; - } - - Action hostsfileAction = getBool( configurationMap, "writeHostsFile", true ) ? Action::WriteEtcHosts : Action::None; - m_actions = hostsfileAction | hostnameAction; } diff --git a/src/modules/users/UsersViewStep.h b/src/modules/users/UsersViewStep.h index 5c3674571..bfc43d1cd 100644 --- a/src/modules/users/UsersViewStep.h +++ b/src/modules/users/UsersViewStep.h @@ -20,8 +20,6 @@ #ifndef USERSPAGEPLUGIN_H #define USERSPAGEPLUGIN_H -#include "SetHostNameJob.h" - #include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/ViewStep.h" @@ -61,8 +59,6 @@ private: UsersPage* m_widget; QList< Calamares::job_ptr > m_jobs; - SetHostNameJob::Actions m_actions; - Config* m_config; }; From fcafe5db8f7434c5efbfadf09e5932f5a136f3e1 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 10:50:38 +0200 Subject: [PATCH 12/91] [users] Test the moved setHostname Config - document that the default for writeHostsFile is *true* --- src/modules/users/Tests.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/modules/users/users.conf | 1 + 2 files changed, 37 insertions(+) diff --git a/src/modules/users/Tests.cpp b/src/modules/users/Tests.cpp index a4ee45fe2..41cd96842 100644 --- a/src/modules/users/Tests.cpp +++ b/src/modules/users/Tests.cpp @@ -25,6 +25,7 @@ // Implementation details extern void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ); +extern HostNameActions getHostNameActions( const QVariantMap& configurationMap ); /** @brief Test Config object methods and internals * @@ -40,6 +41,8 @@ private Q_SLOTS: void initTestCase(); void testDefaultGroups(); + void testHostActions_data(); + void testHostActions(); }; UserTests::UserTests() {} @@ -105,6 +108,39 @@ UserTests::testDefaultGroups() } } +void +UserTests::testHostActions_data() +{ + QTest::addColumn< bool >( "set" ); + QTest::addColumn< QString >( "string" ); + QTest::addColumn< int >( "result" ); + + QTest::newRow( "unset " ) << false << QString() << int( HostNameAction::EtcHostname ); + QTest::newRow( "empty " ) << true << QString() << int( HostNameAction::EtcHostname ); + QTest::newRow( "bad " ) << true << QString( "derp" ) << int( HostNameAction::EtcHostname ); + QTest::newRow( "none " ) << true << QString( "none" ) << int( HostNameAction::None ); + QTest::newRow( "systemd" ) << true << QString( "Hostnamed" ) << int( HostNameAction::SystemdHostname ); +} + +void +UserTests::testHostActions() +{ + QFETCH( bool, set ); + QFETCH( QString, string ); + QFETCH( int, result ); + + QVariantMap m; + if ( set ) + { + m.insert( "setHostname", string ); + } + QCOMPARE( getHostNameActions( m ), HostNameActions( result ) | HostNameAction::WriteEtcHosts ); // write bits default to true + m.insert( "writeHostsFile", false ); + QCOMPARE( getHostNameActions( m ), HostNameActions( result ) ); + m.insert( "writeHostsFile", true ); + QCOMPARE( getHostNameActions( m ), HostNameActions( result ) | HostNameAction::WriteEtcHosts ); +} + QTEST_GUILESS_MAIN( UserTests ) diff --git a/src/modules/users/users.conf b/src/modules/users/users.conf index 32f74978a..259723df2 100644 --- a/src/modules/users/users.conf +++ b/src/modules/users/users.conf @@ -138,4 +138,5 @@ setHostname: EtcFile # Should /etc/hosts be written with a hostname for this machine # (also adds localhost and some ipv6 standard entries). +# Defaults to *true*. writeHostsFile: true From 0ecf1e1cc131d04a0d042e2478ff73b69cbff1e2 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 12:24:39 +0200 Subject: [PATCH 13/91] [users] Drop default parameter for badness --- src/modules/users/UsersPage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/users/UsersPage.cpp b/src/modules/users/UsersPage.cpp index 9054d60e7..9509592ba 100644 --- a/src/modules/users/UsersPage.cpp +++ b/src/modules/users/UsersPage.cpp @@ -50,7 +50,7 @@ enum class Badness /** Add an error message and pixmap to a label. */ static inline void -labelError( QLabel* pix, QLabel* label, const QString& message, Badness bad = Badness::Fatal ) +labelError( QLabel* pix, QLabel* label, const QString& message, Badness bad ) { label->setText( message ); pix->setPixmap( CalamaresUtils::defaultPixmap( ( bad == Badness::Fatal ) ? CalamaresUtils::StatusError @@ -88,7 +88,7 @@ labelStatus( QLabel* pix, QLabel* label, const QString& value, const QString& st } else { - labelError( pix, label, status ); + labelError( pix, label, status, Badness::Fatal ); ok = false; } } @@ -278,7 +278,7 @@ UsersPage::checkPasswordAcceptance( const QString& pw1, const QString& pw2, QLab { if ( pw1 != pw2 ) { - labelError( badge, message, tr( "Your passwords do not match!" ) ); + labelError( badge, message, tr( "Your passwords do not match!" ), Badness::Fatal ); return false; } else From 7b87242107f90388f401ab4650d281d5fd26ba32 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 12:56:09 +0200 Subject: [PATCH 14/91] [users] PW checking does not need widgets --- src/modules/users/CheckPWQuality.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/users/CheckPWQuality.cpp b/src/modules/users/CheckPWQuality.cpp index f06137c9b..33309df6a 100644 --- a/src/modules/users/CheckPWQuality.cpp +++ b/src/modules/users/CheckPWQuality.cpp @@ -22,7 +22,6 @@ #include #include -#include #ifdef HAVE_LIBPWQUALITY #include From 900deb5dc86042a447770a929e377a636d7dcd79 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 13:03:18 +0200 Subject: [PATCH 15/91] [users] Move the configuration of password checks to Config - the Widget (Page) does not need to know the password checks, that's business logic that belongs to Config. --- src/modules/users/Config.cpp | 81 ++++++++++++++++++++++++++++- src/modules/users/Config.h | 14 +++++ src/modules/users/UsersPage.cpp | 68 +++--------------------- src/modules/users/UsersPage.h | 13 ----- src/modules/users/UsersViewStep.cpp | 15 ------ 5 files changed, 102 insertions(+), 89 deletions(-) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index b043691f8..90c301997 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -26,6 +26,7 @@ #include "utils/String.h" #include "utils/Variant.h" +#include #include #include @@ -379,6 +380,27 @@ Config::setRequireStrongPasswords( bool strong ) } } +bool +Config::isPasswordAcceptable(const QString& password, QString& message) +{ + bool failureIsFatal = requireStrongPasswords(); + + for ( auto pc : m_passwordChecks ) + { + QString s = pc.filter( password ); + + if ( !s.isEmpty() ) + { + message = s; + return !failureIsFatal; + } + } + + return true; +} + + + STATICTEST void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ) @@ -409,10 +431,59 @@ getHostNameActions( const QVariantMap& configurationMap ) } } - HostNameAction writeHosts = CalamaresUtils::getBool( configurationMap, "writeHostsFile", true ) ? HostNameAction::WriteEtcHosts : HostNameAction::None; + HostNameAction writeHosts = CalamaresUtils::getBool( configurationMap, "writeHostsFile", true ) + ? HostNameAction::WriteEtcHosts + : HostNameAction::None; return setHostName | writeHosts; } +/** @brief Process entries in the passwordRequirements config entry + * + * Called once for each item in the config entry, which should + * be a key-value pair. What makes sense as a value depends on + * the key. Supported keys are documented in users.conf. + * + * @return if the check was added, returns @c true + */ +STATICTEST bool +addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks ) +{ + if ( key == "minLength" ) + { + add_check_minLength( passwordChecks, value ); + } + else if ( key == "maxLength" ) + { + add_check_maxLength( passwordChecks, value ); + } + else if ( key == "nonempty" ) + { + if ( value.toBool() ) + { + passwordChecks.push_back( + PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is empty" ); }, + []( const QString& s ) { return !s.isEmpty(); }, + PasswordCheck::Weight( 1 ) ) ); + } + else + { + cDebug() << "nonempty check is mentioned but set to false"; + return false; + } + } +#ifdef CHECK_PWQUALITY + else if ( key == "libpwquality" ) + { + add_check_libpwquality( passwordChecks, value ); + } +#endif // CHECK_PWQUALITY + else + { + cWarning() << "Unknown password-check key" << key; + return false; + } + return true; +} void Config::setConfigurationMap( const QVariantMap& configurationMap ) @@ -441,4 +512,12 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) m_permitWeakPasswords = CalamaresUtils::getBool( configurationMap, "allowWeakPasswords", false ); m_requireStrongPasswords = !m_permitWeakPasswords || !CalamaresUtils::getBool( configurationMap, "allowWeakPasswordsDefault", false ); + + // If the value doesn't exist, or isn't a map, this gives an empty map -- no problem + auto pr_checks( configurationMap.value( "passwordRequirements" ).toMap() ); + for ( decltype( pr_checks )::const_iterator i = pr_checks.constBegin(); i != pr_checks.constEnd(); ++i ) + { + addPasswordCheck( i.key(), i.value(), m_passwordChecks ); + } + std::sort( m_passwordChecks.begin(), m_passwordChecks.end() ); } diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h index e61564897..c89ae9bbf 100644 --- a/src/modules/users/Config.h +++ b/src/modules/users/Config.h @@ -21,6 +21,8 @@ #ifndef USERS_CONFIG_H #define USERS_CONFIG_H +#include "CheckPWQuality.h" + #include "utils/NamedEnum.h" #include @@ -110,6 +112,17 @@ public: const QStringList& defaultGroups() const { return m_defaultGroups; } + /** @brief Checks if the password is acceptable. + * + * If all is well, sets @p message to empty and returns @c true. + * If there are warnings, but acceptable, sets @p message to something + * non-empty and returns @c true. This happens if requireStrongPasswords + * is turned off (by config or user). + * If the password is not acceptable, sets @p message to something + * non-empty and returns @c false. + */ + bool isPasswordAcceptable( const QString& password, QString& message ); + static const QStringList& forbiddenLoginNames(); static const QStringList& forbiddenHostNames(); @@ -178,6 +191,7 @@ private: bool m_customHostName = false; HostNameActions m_hostNameActions; + PasswordCheckList m_passwordChecks; }; #endif diff --git a/src/modules/users/UsersPage.cpp b/src/modules/users/UsersPage.cpp index 9509592ba..aca20fd58 100644 --- a/src/modules/users/UsersPage.cpp +++ b/src/modules/users/UsersPage.cpp @@ -283,38 +283,21 @@ UsersPage::checkPasswordAcceptance( const QString& pw1, const QString& pw2, QLab } else { - bool failureIsFatal = ui->checkBoxValidatePassword->isChecked(); - bool failureFound = false; - - if ( m_passwordChecksChanged ) + QString s; + bool ok = m_config->isPasswordAcceptable( pw1, s ); + if ( !ok ) { - std::sort( m_passwordChecks.begin(), m_passwordChecks.end() ); - m_passwordChecksChanged = false; + labelError( badge, message, s, Badness::Fatal ); } - - for ( auto pc : m_passwordChecks ) + else if ( !s.isEmpty() ) { - QString s = pc.filter( pw1 ); - - if ( !s.isEmpty() ) - { - labelError( badge, message, s, failureIsFatal ? Badness::Fatal : Badness::Warning ); - failureFound = true; - if ( failureIsFatal ) - { - return false; - } - } + labelError( badge, message, s, Badness::Warning ); } - - if ( !failureFound ) + else { labelOk( badge, message ); } - - // Here, if failureFound is true then we've found **warnings**, - // which is ok to continue but the user should know. - return true; + return ok; } } @@ -360,38 +343,3 @@ UsersPage::onReuseUserPasswordChanged( const int checked ) ui->textBoxVerifiedRootPassword->setVisible( visible ); checkReady( isReady() ); } - -void -UsersPage::addPasswordCheck( const QString& key, const QVariant& value ) -{ - m_passwordChecksChanged = true; - - if ( key == "minLength" ) - { - add_check_minLength( m_passwordChecks, value ); - } - else if ( key == "maxLength" ) - { - add_check_maxLength( m_passwordChecks, value ); - } - else if ( key == "nonempty" ) - { - if ( value.toBool() ) - { - m_passwordChecks.push_back( - PasswordCheck( []() { return QCoreApplication::translate( "PWQ", "Password is empty" ); }, - []( const QString& s ) { return !s.isEmpty(); }, - PasswordCheck::Weight( 1 ) ) ); - } - } -#ifdef CHECK_PWQUALITY - else if ( key == "libpwquality" ) - { - add_check_libpwquality( m_passwordChecks, value ); - } -#endif // CHECK_PWQUALITY - else - { - cWarning() << "Unknown password-check key" << key; - } -} diff --git a/src/modules/users/UsersPage.h b/src/modules/users/UsersPage.h index 5e0909b54..2f446b563 100644 --- a/src/modules/users/UsersPage.h +++ b/src/modules/users/UsersPage.h @@ -24,8 +24,6 @@ #ifndef USERSPAGE_H #define USERSPAGE_H -#include "CheckPWQuality.h" - #include class Config; @@ -50,14 +48,6 @@ public: void onActivate(); - /** @brief Process entries in the passwordRequirements config entry - * - * Called once for each item in the config entry, which should - * be a key-value pair. What makes sense as a value depends on - * the key. Supported keys are documented in users.conf. - */ - void addPasswordCheck( const QString& key, const QVariant& value ); - ///@brief Root password, depends on settings, may be empty QString getRootPassword() const; ///@brief User name and password @@ -88,9 +78,6 @@ private: Ui::Page_UserSetup* ui; Config* m_config; - PasswordCheckList m_passwordChecks; - bool m_passwordChecksChanged = false; - bool m_readyFullName; bool m_readyUsername; bool m_readyHostname; diff --git a/src/modules/users/UsersViewStep.cpp b/src/modules/users/UsersViewStep.cpp index 300bdf072..75c76bd21 100644 --- a/src/modules/users/UsersViewStep.cpp +++ b/src/modules/users/UsersViewStep.cpp @@ -151,19 +151,4 @@ void UsersViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { m_config->setConfigurationMap( configurationMap ); - - // Create the widget, after all .. as long as writing configuration to the UI is needed - (void)this->widget(); - using CalamaresUtils::getBool; - - if ( configurationMap.contains( "passwordRequirements" ) - && configurationMap.value( "passwordRequirements" ).type() == QVariant::Map ) - { - auto pr_checks( configurationMap.value( "passwordRequirements" ).toMap() ); - - for ( decltype( pr_checks )::const_iterator i = pr_checks.constBegin(); i != pr_checks.constEnd(); ++i ) - { - m_widget->addPasswordCheck( i.key(), i.value() ); - } - } } From b2b9ae779932643afbb7ff3e7e80a89bfe76d759 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 13:03:56 +0200 Subject: [PATCH 16/91] [users] Add tests for moved password-check configuration - link the PW checks to the test, and libpwquality if needed - test only does very basic config-mungeing --- src/modules/users/CMakeLists.txt | 3 +++ src/modules/users/Tests.cpp | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/modules/users/CMakeLists.txt b/src/modules/users/CMakeLists.txt index 5fafcd4f3..95b3a9697 100644 --- a/src/modules/users/CMakeLists.txt +++ b/src/modules/users/CMakeLists.txt @@ -71,4 +71,7 @@ calamares_add_test( SOURCES Tests.cpp Config.cpp + CheckPWQuality.cpp + LIBRARIES + ${USER_EXTRA_LIB} ) diff --git a/src/modules/users/Tests.cpp b/src/modules/users/Tests.cpp index 41cd96842..ff435803d 100644 --- a/src/modules/users/Tests.cpp +++ b/src/modules/users/Tests.cpp @@ -26,6 +26,7 @@ // Implementation details extern void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ); extern HostNameActions getHostNameActions( const QVariantMap& configurationMap ); +extern bool addPasswordCheck( const QString& key, const QVariant& value, PasswordCheckList& passwordChecks ); /** @brief Test Config object methods and internals * @@ -43,6 +44,7 @@ private Q_SLOTS: void testDefaultGroups(); void testHostActions_data(); void testHostActions(); + void testPasswordChecks(); }; UserTests::UserTests() {} @@ -141,6 +143,19 @@ UserTests::testHostActions() QCOMPARE( getHostNameActions( m ), HostNameActions( result ) | HostNameAction::WriteEtcHosts ); } +void +UserTests::testPasswordChecks() +{ + { + PasswordCheckList l; + QCOMPARE( l.length(), 0 ); + QVERIFY( !addPasswordCheck( "nonempty", QVariant(false), l ) ); // a silly setting + QCOMPARE( l.length(), 0 ); + QVERIFY( addPasswordCheck( "nonempty", QVariant(true), l ) ); + QCOMPARE( l.length(), 1 ); + } +} + QTEST_GUILESS_MAIN( UserTests ) From eb72d662d10b6b94b3ce89f9e2eeb0cd968849ba Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 13:29:06 +0200 Subject: [PATCH 17/91] [users] Add password fields to Config - no checking is done for validity, and there is no password-status --- src/modules/users/Config.cpp | 38 +++++++++++++++++++++++++++++++++++- src/modules/users/Config.h | 28 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index 90c301997..ea3aa5221 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -381,7 +381,7 @@ Config::setRequireStrongPasswords( bool strong ) } bool -Config::isPasswordAcceptable(const QString& password, QString& message) +Config::isPasswordAcceptable( const QString& password, QString& message ) { bool failureIsFatal = requireStrongPasswords(); @@ -399,7 +399,43 @@ Config::isPasswordAcceptable(const QString& password, QString& message) return true; } +void +Config::setUserPassword( const QString& s ) +{ + m_userPassword = s; + // TODO: check new password status + emit userPasswordChanged( s ); +} +void +Config::setUserPasswordSecondary( const QString& s ) +{ + m_userPasswordSecondary = s; + // TODO: check new password status + emit userPasswordSecondaryChanged( s ); +} + +void +Config::setRootPassword( const QString& s ) +{ + if ( writeRootPassword() ) + { + m_rootPassword = s; + // TODO: check new password status + emit rootPasswordChanged( s ); + } +} + +void +Config::setRootPasswordSecondary( const QString& s ) +{ + if ( writeRootPassword() ) + { + m_rootPasswordSecondary = s; + // TODO: check new password status + emit rootPasswordSecondaryChanged( s ); + } +} STATICTEST void diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h index c89ae9bbf..5177542c2 100644 --- a/src/modules/users/Config.h +++ b/src/modules/users/Config.h @@ -59,6 +59,13 @@ class Config : public QObject Q_PROPERTY( QString hostNameStatus READ hostNameStatus NOTIFY hostNameStatusChanged ) Q_PROPERTY( HostNameActions hostNameActions READ hostNameActions CONSTANT ) + Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged ) + Q_PROPERTY( QString userPasswordSecondary READ userPasswordSecondary WRITE setUserPasswordSecondary NOTIFY + userPasswordSecondaryChanged ) + Q_PROPERTY( QString rootPassword READ rootPassword WRITE setRootPassword NOTIFY rootPasswordChanged ) + Q_PROPERTY( QString rootPasswordSecondary READ rootPasswordSecondary WRITE setRootPasswordSecondary NOTIFY + rootPasswordSecondaryChanged ) + Q_PROPERTY( bool writeRootPassword READ writeRootPassword CONSTANT ) Q_PROPERTY( bool reuseUserPasswordForRoot READ reuseUserPasswordForRoot WRITE setReuseUserPasswordForRoot NOTIFY reuseUserPasswordForRootChanged ) @@ -123,6 +130,11 @@ public: */ bool isPasswordAcceptable( const QString& password, QString& message ); + QString userPassword() const { return m_userPassword; } + QString userPasswordSecondary() const { return m_userPasswordSecondary; } + QString rootPassword() const { return m_rootPassword; } + QString rootPasswordSecondary() const { return m_rootPasswordSecondary; } + static const QStringList& forbiddenLoginNames(); static const QStringList& forbiddenHostNames(); @@ -158,6 +170,11 @@ public Q_SLOTS: /// Change setting for "require strong password" void setRequireStrongPasswords( bool strong ); + void setUserPassword( const QString& ); + void setUserPasswordSecondary( const QString& ); + void setRootPassword( const QString& ); + void setRootPasswordSecondary( const QString& ); + signals: void userShellChanged( const QString& ); void autologinGroupChanged( const QString& ); @@ -170,6 +187,11 @@ signals: void autoLoginChanged( bool ); void reuseUserPasswordForRootChanged( bool ); void requireStrongPasswordsChanged( bool ); + void userPasswordChanged( const QString& ); + void userPasswordSecondaryChanged( const QString& ); + void rootPasswordChanged( const QString& ); + void rootPasswordSecondaryChanged( const QString& ); + private: QStringList m_defaultGroups; @@ -179,6 +201,12 @@ private: QString m_fullName; QString m_loginName; QString m_hostName; + + QString m_userPassword; + QString m_userPasswordSecondary; // enter again to be sure + QString m_rootPassword; + QString m_rootPasswordSecondary; + bool m_doAutoLogin = false; bool m_writeRootPassword = true; From b49b9a66e6af900f0102ccddd2d2102c648009b8 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 13:42:18 +0200 Subject: [PATCH 18/91] [users] Drop data-access from the Page - get username, password etc. from the config object, not the page - jobs now depend entirely on config - handle logic of "what's the root password" in Config --- src/modules/users/Config.cpp | 22 ++++++++++++++++++++++ src/modules/users/Config.h | 8 ++++++-- src/modules/users/UsersPage.cpp | 26 -------------------------- src/modules/users/UsersPage.h | 5 ----- src/modules/users/UsersViewStep.cpp | 5 ++--- 5 files changed, 30 insertions(+), 36 deletions(-) diff --git a/src/modules/users/Config.cpp b/src/modules/users/Config.cpp index ea3aa5221..69712a733 100644 --- a/src/modules/users/Config.cpp +++ b/src/modules/users/Config.cpp @@ -437,6 +437,28 @@ Config::setRootPasswordSecondary( const QString& s ) } } +QString Config::rootPassword() const +{ + if ( writeRootPassword() ) + { + if ( reuseUserPasswordForRoot() ) + return userPassword(); + return m_rootPassword; + } + return QString(); +} + +QString Config::rootPasswordSecondary() const +{ + if ( writeRootPassword() ) + { + if ( reuseUserPasswordForRoot() ) + return userPasswordSecondary(); + return m_rootPasswordSecondary; + } + return QString(); +} + STATICTEST void setConfigurationDefaultGroups( const QVariantMap& map, QStringList& defaultGroups ) diff --git a/src/modules/users/Config.h b/src/modules/users/Config.h index 5177542c2..334bfcdb3 100644 --- a/src/modules/users/Config.h +++ b/src/modules/users/Config.h @@ -130,10 +130,14 @@ public: */ bool isPasswordAcceptable( const QString& password, QString& message ); + // The user enters a password (and again in a separate UI element) QString userPassword() const { return m_userPassword; } QString userPasswordSecondary() const { return m_userPasswordSecondary; } - QString rootPassword() const { return m_rootPassword; } - QString rootPasswordSecondary() const { return m_rootPasswordSecondary; } + // The root password **may** be entered in the UI, or may be suppressed + // entirely when writeRootPassword is off, or may be equal to + // the user password when reuseUserPasswordForRoot is on. + QString rootPassword() const; + QString rootPasswordSecondary() const; static const QStringList& forbiddenLoginNames(); static const QStringList& forbiddenHostNames(); diff --git a/src/modules/users/UsersPage.cpp b/src/modules/users/UsersPage.cpp index aca20fd58..5a96af4e4 100644 --- a/src/modules/users/UsersPage.cpp +++ b/src/modules/users/UsersPage.cpp @@ -199,32 +199,6 @@ UsersPage::isReady() const return readyFields; } -QString -UsersPage::getRootPassword() const -{ - if ( m_config->writeRootPassword() ) - { - if ( ui->checkBoxReusePassword->isChecked() ) - { - return ui->textBoxUserPassword->text(); - } - else - { - return ui->textBoxRootPassword->text(); - } - } - else - { - return QString(); - } -} - -QPair< QString, QString > -UsersPage::getUserPassword() const -{ - return QPair< QString, QString >( m_config->loginName(), ui->textBoxUserPassword->text() ); -} - void UsersPage::fillGlobalStorage() const { diff --git a/src/modules/users/UsersPage.h b/src/modules/users/UsersPage.h index 2f446b563..46c4ed399 100644 --- a/src/modules/users/UsersPage.h +++ b/src/modules/users/UsersPage.h @@ -48,11 +48,6 @@ public: void onActivate(); - ///@brief Root password, depends on settings, may be empty - QString getRootPassword() const; - ///@brief User name and password - QPair< QString, QString > getUserPassword() const; - protected slots: void onFullNameTextEdited( const QString& ); void reportLoginNameStatus( const QString& ); diff --git a/src/modules/users/UsersViewStep.cpp b/src/modules/users/UsersViewStep.cpp index 75c76bd21..2733539b6 100644 --- a/src/modules/users/UsersViewStep.cpp +++ b/src/modules/users/UsersViewStep.cpp @@ -132,11 +132,10 @@ UsersViewStep::onLeave() m_config->doAutoLogin(), m_config->defaultGroups() ); - auto userPW = m_widget->getUserPassword(); - j = new SetPasswordJob( userPW.first, userPW.second ); + j = new SetPasswordJob( m_config->loginName(), m_config->userPassword() ); m_jobs.append( Calamares::job_ptr( j ) ); - j = new SetPasswordJob( "root", m_widget->getRootPassword() ); + j = new SetPasswordJob( "root", m_config->rootPassword() ); m_jobs.append( Calamares::job_ptr( j ) ); // TODO: Config object should create jobs From ee3f30868659c16555226d7710ba94b422cee72c Mon Sep 17 00:00:00 2001 From: Calamares CI Date: Wed, 5 Aug 2020 13:53:54 +0200 Subject: [PATCH 19/91] i18n: [calamares] Automatic merge of Transifex translations --- lang/calamares_ast.ts | 25 ++- lang/calamares_bn.ts | 485 +++++++++++++++++++++--------------------- lang/calamares_da.ts | 9 +- lang/calamares_hi.ts | 23 +- lang/calamares_sv.ts | 84 ++++---- 5 files changed, 320 insertions(+), 306 deletions(-) diff --git a/lang/calamares_ast.ts b/lang/calamares_ast.ts index fb9402c5c..f10b353e9 100644 --- a/lang/calamares_ast.ts +++ b/lang/calamares_ast.ts @@ -776,22 +776,22 @@ L'instalador va colar y van perdese tolos cambeos. <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Afáyate nel programa de configuración de Calamares pa %1</h1> <h1>Welcome to %1 setup</h1> - + <h1>Afáyate na configuración de %1</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>Afáyate nel instalador Calamares pa %1</h1> <h1>Welcome to the %1 installer</h1> - + <h1>Afáyate nel instalador de %1</h1> @@ -1432,7 +1432,7 @@ L'instalador va colar y van perdese tolos cambeos. is running the installer as an administrator (root) - + ta executando l'instalador como alministrador (root) @@ -1447,7 +1447,7 @@ L'instalador va colar y van perdese tolos cambeos. has a screen large enough to show the whole installer - + tien una pantalla abondo grande como p'amosar tol instalador @@ -1884,7 +1884,7 @@ L'instalador va colar y van perdese tolos cambeos. Theming - + Estilu @@ -2851,7 +2851,8 @@ Salida: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Esti ordenador nun satisfaz nengún de los requirimientos aconseyaos pa configurar %1.<br/> + La configuración pue siguir pero quiciabes se desactiven dalgunes carauterístiques.</p> @@ -2962,13 +2963,15 @@ Salida: <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + <p>Esti ordenador nun satisfaz los requirimientos mínimos pa instalar %1.<br/> + La instalación nun pue siguir.</p> <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Esti ordenador nun satisfaz nengún de los requirimientos aconseyaos pa configurar %1.<br/> + La configuración pue siguir pero quiciabes se desactiven dalgunes carauterístiques.</p> @@ -3517,7 +3520,7 @@ Salida: By selecting this you will send information about your installation and hardware. This information will only be sent <b>once</b> after the installation finishes. - + Al esbillar esto vas unviar información tocante a la instalación y el hardware. Esta información va unviase namás <b>una vegada</b> dempués de finar la instalación. diff --git a/lang/calamares_bn.ts b/lang/calamares_bn.ts index aec314a22..1c279ae56 100644 --- a/lang/calamares_bn.ts +++ b/lang/calamares_bn.ts @@ -6,17 +6,17 @@ The <strong>boot environment</strong> of this system.<br><br>Older x86 systems only support <strong>BIOS</strong>.<br>Modern systems usually use <strong>EFI</strong>, but may also show up as BIOS if started in compatibility mode. - + এই সিস্টেমের <strong>বুট পরিবেশ</strong>।<br><br> পুরাতন x86 সিস্টেম শুধুমাত্র <strong>BIOS</strong> সমর্থন কর<br> আধুনিক সিস্টেম সাধারণত <strong>EFI</strong> ব্যবহার করে, কিন্তু যদি সামঞ্জস্যতা মোডে শুরু হয় তাহলে BIOS হিসেবেও প্রদর্শিত হতে পারে। This system was started with an <strong>EFI</strong> boot environment.<br><br>To configure startup from an EFI environment, this installer must deploy a boot loader application, like <strong>GRUB</strong> or <strong>systemd-boot</strong> on an <strong>EFI System Partition</strong>. This is automatic, unless you choose manual partitioning, in which case you must choose it or create it on your own. - + এই সিস্টেম একটি <strong>EFI</strong> বুট পরিবেশ দিয়ে শুরু হয়েছিল।<br><br> একটি EFI পরিবেশ থেকে স্টার্টআপ কনফিগার করতে, এই ইনস্টলার অবশ্যই একটি <strong>EFI সিস্টেম পার্টিশনে</strong> <strong>GRUB</strong> বা <strong>systemd-boot </strong> এর মত একটি বুট লোডার অ্যাপ্লিকেশন প্রয়োগ করতে হবে। এটি স্বয়ংক্রিয়, যদি না আপনি ম্যানুয়াল পার্টিশনিং নির্বাচন করেন, সেক্ষেত্রে আপনাকে অবশ্যই এটি বেছে নিতে হবে অথবা এটি নিজে তৈরি করতে হবে। This system was started with a <strong>BIOS</strong> boot environment.<br><br>To configure startup from a BIOS environment, this installer must install a boot loader, like <strong>GRUB</strong>, either at the beginning of a partition or on the <strong>Master Boot Record</strong> near the beginning of the partition table (preferred). This is automatic, unless you choose manual partitioning, in which case you must set it up on your own. - + এই সিস্টেম একটি <strong>BIOS</strong> বুট পরিবেশ দিয়ে শুরু হয়েছিল।<br><br>একটি BIOS পরিবেশ থেকে স্টার্টআপ কনফিগার করতে, এই ইনস্টলার অবশ্যই GRUB এর মত একটি পার্টিশনের শুরুতে অথবা পার্টিশন টেবিলের শুরুতে মাস্টার বুট রেকর্ডে (পছন্দনীয়) মত একটি বুট লোডার ইনস্টল করতে হবে। এটি স্বয়ংক্রিয়, যদি না আপনি ম্যানুয়াল পার্টিশনিং নির্বাচন করেন, সেক্ষেত্রে আপনাকে অবশ্যই এটি নিজেই সেট আপ করতে হবে। @@ -39,12 +39,12 @@ Do not install a boot loader - + একটি বুট লোডার ইনস্টল করবেন না %1 (%2) - + %1 (%2) @@ -60,22 +60,22 @@ Form - + ফর্ম GlobalStorage - + গ্লোবাল স্টোরেজ JobQueue - + জব লাইন Modules - + মডিউলগুলো @@ -111,7 +111,7 @@ Debug information - + তথ্য ডিবাগ করুন @@ -124,7 +124,7 @@ Install - + ইনস্টল করুন @@ -171,7 +171,7 @@ Running command %1 %2 - + কমান্ড %1 %2 চলছে @@ -179,22 +179,22 @@ Running %1 operation. - + %1 ক্রিয়াকলাপ চলছে। Bad working directory path - খারাপ ওয়ার্কিং ডিরেক্টরি পাথ + ওয়ার্কিং ডিরেক্টরি পাথ ভালো নয় Working directory %1 for python job %2 is not readable. - ওয়ার্কিং ডিরেক্টরি 1% পাইথন কাজের জন্য % 2 পাঠযোগ্য নয়। + ওয়ার্কিং ডিরেক্টরি 1% পাইথন কাজের জন্য %2 পাঠযোগ্য নয়। Bad main script file - খারাপ প্রধান স্ক্রিপ্ট ফাইল + প্রধান স্ক্রিপ্ট ফাইল ভালো নয় @@ -264,7 +264,7 @@ Installation Failed - ইনস্টলেশন ব্যর্থ হয়েছে + ইনস্টলেশন ব্যর্থ হলো @@ -321,7 +321,7 @@ Continue with setup? - + সেটআপ চালিয়ে যেতে চান? @@ -336,7 +336,7 @@ The %1 installer is about to make changes to your disk in order to install %2.<br/><strong>You will not be able to undo these changes.</strong> - + %1 ইনস্টলার %2 সংস্থাপন করতে আপনার ডিস্কে পরিবর্তন করতে যাচ্ছে। @@ -346,12 +346,12 @@ &Install now - + এবংএখনই ইনস্টল করুন Go &back - + এবংফিরে যান @@ -401,7 +401,7 @@ &Cancel - + এবংবাতিল করুন @@ -411,7 +411,7 @@ Cancel installation? - + ইনস্টলেশন বাতিল করবেন? @@ -423,7 +423,8 @@ The setup program will quit and all changes will be lost. Do you really want to cancel the current install process? The installer will quit and all changes will be lost. - + আপনি কি সত্যিই বর্তমান সংস্থাপন প্রক্রিয়া বাতিল করতে চান? +ইনস্টলার টি বন্ধ হয়ে যাবে এবং সকল পরিবর্তন হারিয়ে যাবে। @@ -431,22 +432,22 @@ The installer will quit and all changes will be lost. Unknown exception type - অজানা ব্যতিক্রম প্রকার + অজানা ধরনের ব্যতিক্রম unparseable Python error - আনপারসেবল পাইথন ত্রুটি + অতুলনীয় পাইথন ত্রুটি unparseable Python traceback - আনপারসেবল পাইথন ট্রেসব্যাক + অতুলনীয় পাইথন ট্রেসব্যাক Unfetchable Python error. - অপরিবর্তনীয় পাইথন ত্রুটি। + অতুলনীয় পাইথন ত্রুটি। @@ -463,7 +464,7 @@ The installer will quit and all changes will be lost. Show debug information - + ডিবাগ তথ্য দেখান @@ -478,7 +479,7 @@ The installer will quit and all changes will be lost. &Cancel - + এবংবাতিল করুন @@ -496,7 +497,7 @@ The installer will quit and all changes will be lost. Gathering system information... - + সিস্টেম তথ্য সংগ্রহ করা হচ্ছে... @@ -504,12 +505,12 @@ The installer will quit and all changes will be lost. Form - + ফর্ম Select storage de&vice: - + স্টোরেজ ডিএবংভাইস নির্বাচন করুন: @@ -517,12 +518,12 @@ The installer will quit and all changes will be lost. Current: - + বর্তমান: After: - + পরে: @@ -537,7 +538,7 @@ The installer will quit and all changes will be lost. <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + <strong>সংকুচিত করার জন্য একটি পার্টিশন নির্বাচন করুন, তারপর নিচের বারটি পুনঃআকারের জন্য টেনে আনুন</strong> @@ -547,12 +548,12 @@ The installer will quit and all changes will be lost. Boot loader location: - + বুট লোডার অবস্থান: <strong>Select a partition to install on</strong> - + <strong>ইনস্টল করতে একটি পার্টিশন নির্বাচন করুন</strong> @@ -562,17 +563,17 @@ The installer will quit and all changes will be lost. The EFI system partition at %1 will be used for starting %2. - + %1 এ EFI সিস্টেম পার্টিশন %2 শুরু করার জন্য ব্যবহার করা হবে। EFI system partition: - + EFI সিস্টেম পার্টিশন: This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই স্টোরেজ ডিভাইসে কোন অপারেটিং সিস্টেম আছে বলে মনে হয় না। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন। @@ -580,7 +581,7 @@ The installer will quit and all changes will be lost. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - + <strong>ডিস্ক মুছে ফেলুন</strong> <br/>এটি বর্তমানে নির্বাচিত স্টোরেজ ডিভাইসে উপস্থিত সকল উপাত্ত <font color="red">মুছে ফেলবে</font>। @@ -588,7 +589,7 @@ The installer will quit and all changes will be lost. <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. - + <strong>ইনস্টল করুন পাশাপাশি</strong> <br/>ইনস্টলার %1 এর জন্য জায়গা তৈরি করতে একটি পার্টিশন সংকুচিত করবে। @@ -596,22 +597,22 @@ The installer will quit and all changes will be lost. <strong>Replace a partition</strong><br/>Replaces a partition with %1. - + <strong>একটি পার্টিশন প্রতিস্থাপন করুন</strong><br/>%1-এর সাথে একটি পার্টিশন প্রতিস্থাপন করে। This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই সঞ্চয় যন্ত্রটিতে %1 আছে। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন। This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই স্টোরেজ ডিভাইসে ইতোমধ্যে একটি অপারেটিং সিস্টেম আছে। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন. This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + এই স্টোরেজ ডিভাইসে একাধিক অপারেটিং সিস্টেম আছে। তুমি কি করতে চাও? <br/>স্টোরেজ ডিভাইসে কোন পরিবর্তন করার আগে আপনি আপনার পছন্দপর্যালোচনা এবং নিশ্চিত করতে সক্ষম হবেন. @@ -644,17 +645,17 @@ The installer will quit and all changes will be lost. Clear mounts for partitioning operations on %1 - + %1 এ পার্টিশনিং অপারেশনের জন্য মাউন্ট গুলি মুছে ফেলুন Clearing mounts for partitioning operations on %1. - + %1-এ পার্টিশনিং অপারেশনের জন্য মাউন্ট মুছে ফেলা হচ্ছে। Cleared all mounts for %1 - + %1-এর জন্য সকল মাউন্ট মুছে ফেলা হয়েছে @@ -662,22 +663,22 @@ The installer will quit and all changes will be lost. Clear all temporary mounts. - + সব অস্থায়ী মাউন্ট পরিষ্কার করুন। Clearing all temporary mounts. - + সব অস্থায়ী মাউন্ট পরিষ্কার করা হচ্ছে। Cannot get list of temporary mounts. - + অস্থায়ী মাউন্টের তালিকা পাওয়া যাচ্ছে না। Cleared all temporary mounts. - + সব অস্থায়ী মাউন্ট পরিষ্কার করা হয়েছে. @@ -704,12 +705,12 @@ The installer will quit and all changes will be lost. Set keyboard model to %1.<br/> - + %1-এ কীবোর্ড নকশা নির্ধারণ করুন। Set keyboard layout to %1/%2. - + %1/%2 এ কীবোর্ড বিন্যাস নির্ধারণ করুন। @@ -850,7 +851,7 @@ The installer will quit and all changes will be lost. Si&ze: - + আএবংকার @@ -860,22 +861,22 @@ The installer will quit and all changes will be lost. Partition &Type: - পার্টিশন এবং প্রকার: + পার্টিশন এবংধরন: &Primary - এবং প্রাথমিক + এবংপ্রাথমিক E&xtended - সম্প্রসারিত + বএবংর্ধিত Fi&le System: - + ফাএবংইল সিস্টেম: @@ -885,7 +886,7 @@ The installer will quit and all changes will be lost. &Mount Point: - এবং মাউন্ট পয়েন্ট: + এবংমাউন্ট পয়েন্ট: @@ -900,17 +901,17 @@ The installer will quit and all changes will be lost. Logical - + যৌক্তিক Primary - + প্রাথমিক GPT - + জিপিটি @@ -933,12 +934,12 @@ The installer will quit and all changes will be lost. Creating new %1 partition on %2. - + %2-এ নতুন %1 পার্টিশন তৈরি করা হচ্ছে। The installer failed to create partition on disk '%1'. - + ইনস্টলার '%1' ডিস্কে পার্টিশন তৈরি করতে ব্যর্থ হয়েছে। @@ -946,27 +947,27 @@ The installer will quit and all changes will be lost. Create Partition Table - + পার্টিশন টেবিল তৈরি করুন Creating a new partition table will delete all existing data on the disk. - + একটি নতুন পার্টিশন টেবিল তৈরি করলে ডিস্কের সকল বিদ্যমান উপাত্ত মুছে যাবে। What kind of partition table do you want to create? - + আপনি কি ধরনের পার্টিশন টেবিল তৈরি করতে চান? Master Boot Record (MBR) - + মাস্টার বুট রেকর্ড (এমবিআর) GUID Partition Table (GPT) - + জিইউআইডি পার্টিশন টেবিল (জিপিটি) @@ -974,22 +975,22 @@ The installer will quit and all changes will be lost. Create new %1 partition table on %2. - + %2-এ নতুন %1 পার্টিশন টেবিল তৈরি করুন। Create new <strong>%1</strong> partition table on <strong>%2</strong> (%3). - + <strong>%2</strong> (%3) এ নতুন <strong>%1</strong> পার্টিশন টেবিল তৈরি করুন। Creating new %1 partition table on %2. - + %2 এ নতুন %1 পার্টিশন টেবিল তৈরি করা হচ্ছে। The installer failed to create a partition table on %1. - + ইনস্টলার %1 এ একটি পার্টিশন টেবিল তৈরি করতে ব্যর্থ হয়েছে। @@ -997,27 +998,27 @@ The installer will quit and all changes will be lost. Create user %1 - + %1 ব্যবহারকারী তৈরি করুন Create user <strong>%1</strong>. - + ব্যবহারকারী %1 তৈরি করুন। Creating user %1. - + ব্যবহারকারী %1 তৈরি করা হচ্ছে। Cannot create sudoers file for writing. - + লেখার জন্য sudoers ফাইল তৈরি করা যাবে না। Cannot chmod sudoers file. - + Sudoers ফাইল chmod করা যাবে না। @@ -1075,22 +1076,22 @@ The installer will quit and all changes will be lost. Delete partition %1. - + পার্টিশন %1 মুছে ফেলুন। Delete partition <strong>%1</strong>. - + পার্টিশন <strong>%1</strong> মুছে ফেলুন। Deleting partition %1. - + পার্টিশন %1 মুছে ফেলা হচ্ছে। The installer failed to delete partition %1. - + ইনস্টলার %1 পার্টিশন মুছে ফেলতে ব্যর্থ হয়েছে। @@ -1098,22 +1099,22 @@ The installer will quit and all changes will be lost. This device has a <strong>%1</strong> partition table. - + এই যন্ত্রটির একটি <strong>%1</strong> পার্টিশন টেবিল আছে। This is a <strong>loop</strong> device.<br><br>It is a pseudo-device with no partition table that makes a file accessible as a block device. This kind of setup usually only contains a single filesystem. - + এটি একটি <strong>লুপ</strong> ডিভাইস।<br><br>এটি একটি ছদ্ম-ডিভাইস যার কোন পার্টিশন টেবিল নেই যা একটি ফাইলকে ব্লক ডিভাইস হিসেবে অ্যাক্সেসযোগ্য করে তোলে। এই ধরনের উপস্থাপনা সাধারণত শুধুমাত্র একটি একক ফাইলসিস্টেম ধারণ করে। This installer <strong>cannot detect a partition table</strong> on the selected storage device.<br><br>The device either has no partition table, or the partition table is corrupted or of an unknown type.<br>This installer can create a new partition table for you, either automatically, or through the manual partitioning page. - + এই ইনস্টলার নির্বাচিত সঞ্চয় ডিভাইসে <strong>একটি পার্টিশন টেবিল শনাক্ত করতে পারে না</strong>।<br> <br>ডিভাইসটির কোন পার্টিশন টেবিল নেই, অথবা পার্টিশন টেবিলটি দূষিত অথবা একটি অজানা ধরনের।<br> এই ইনস্টলার আপনার জন্য একটি নতুন পার্টিশন টেবিল তৈরি করতে পারে, হয় স্বয়ংক্রিয়ভাবে, অথবা ম্যানুয়াল পার্টিশনিং পৃষ্ঠার মাধ্যমে। <br><br>This is the recommended partition table type for modern systems which start from an <strong>EFI</strong> boot environment. - + <br><br>এটি আধুনিক সিস্টেমের জন্য প্রস্তাবিত পার্টিশন টেবিলের ধরন যা একটি <strong>EFI</strong> বুট পরিবেশ থেকে শুরু হয়। @@ -1123,7 +1124,7 @@ The installer will quit and all changes will be lost. The type of <strong>partition table</strong> on the selected storage device.<br><br>The only way to change the partition table type is to erase and recreate the partition table from scratch, which destroys all data on the storage device.<br>This installer will keep the current partition table unless you explicitly choose otherwise.<br>If unsure, on modern systems GPT is preferred. - + নির্বাচিত স্টোরেজ ডিভাইসে <strong>পার্টিশন টেবিলে</strong>র ধরন। <br><br>পার্টিশন টেবিলের ধরন পরিবর্তনের একমাত্র উপায় হল স্ক্র্যাচ থেকে পার্টিশন টেবিল মুছে ফেলা এবং পুনরায় তৈরি করা, যা স্টোরেজ ডিভাইসের সমস্ত ডাটা ধ্বংস করে।<br> এই ইনস্টলার বর্তমান পার্টিশন টেবিল রাখবে যদি না আপনি স্পষ্টভাবে অন্যভাবে নির্বাচন করেন. যদি অনিশ্চিত থাকেন, আধুনিক সিস্টেমে জিপিটি অগ্রাধিকার দেওয়া হয়। @@ -1132,7 +1133,7 @@ The installer will quit and all changes will be lost. %1 - %2 (%3) device[name] - size[number] (device-node[name]) - + %1 - %2 (%3) @@ -1172,27 +1173,27 @@ The installer will quit and all changes will be lost. Edit Existing Partition - + বিদ্যমান পার্টিশন সম্পাদনা করুন Content: - + বিষয়বস্তু: &Keep - + এবংরাখুন Format - + বিন্যাস Warning: Formatting the partition will erase all existing data. - + সতর্কীকরণ: পার্টিশন ফরম্যাট করলে সমস্ত বিদ্যমান উপাত্ত মুছে ফেলবে। @@ -1202,7 +1203,7 @@ The installer will quit and all changes will be lost. Si&ze: - + আএবংকার @@ -1212,7 +1213,7 @@ The installer will quit and all changes will be lost. Fi&le System: - + ফাএবংইল সিস্টেম: @@ -1230,7 +1231,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -1258,37 +1259,37 @@ The installer will quit and all changes will be lost. Set partition information - + পার্টিশন তথ্য নির্ধারণ করুন Install %1 on <strong>new</strong> %2 system partition. - + <strong>নতুন</strong> %2 সিস্টেম পার্টিশনে %1 সংস্থাপন করুন। Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong>. - + মাউন্ট বিন্দু <strong>%1</strong> দিয়ে <strong>নতুন</strong> %2 পার্টিশন বিন্যাস করুন। Install %2 on %3 system partition <strong>%1</strong>. - + %3 সিস্টেম পার্টিশন <strong>%1</strong> এ %2 ইনস্টল করুন। Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong>. - + %3 পার্টিশন <strong>%1</strong> মাউন্ট বিন্দু <strong>%2</strong> দিয়ে বিন্যাস করুন। Install boot loader on <strong>%1</strong>. - + <strong>%1</strong> এ বুট লোডার ইনস্টল করুন। Setting up mount points. - + মাউন্ট পয়েন্ট সেট আপ করা হচ্ছে। @@ -1296,12 +1297,12 @@ The installer will quit and all changes will be lost. Form - + ফর্ম &Restart now - + এবংএখন আবার চালু করুন @@ -1316,7 +1317,7 @@ The installer will quit and all changes will be lost. <h1>All done.</h1><br/>%1 has been installed on your computer.<br/>You may now restart into your new system, or continue using the %2 Live environment. - + <h1>সব শেষ।</h1><br/>%1 আপনার কম্পিউটারে সংস্থাপন করা হয়েছে।<br/>আপনি এখন আপনার নতুন সিস্টেমে পুনর্সূচনা করতে পারেন, অথবা %2 লাইভ পরিবেশ ব্যবহার চালিয়ে যেতে পারেন। @@ -1339,7 +1340,7 @@ The installer will quit and all changes will be lost. Finish - + শেষ করুন @@ -1377,12 +1378,12 @@ The installer will quit and all changes will be lost. Formatting partition %1 with file system %2. - + ফাইল সিস্টেম %2 দিয়ে পার্টিশন %1 বিন্যাস করা হচ্ছে। The installer failed to format partition %1 on disk '%2'. - + ইনস্টলার '%2' ডিস্কে %1 পার্টিশন বিন্যাস করতে ব্যর্থ হয়েছে। @@ -1523,7 +1524,7 @@ The installer will quit and all changes will be lost. Executing script: &nbsp;<code>%1</code> - + স্ক্রিপ্ট কার্যকর করা হচ্ছে: &nbsp;<code>%1</code> @@ -1531,7 +1532,7 @@ The installer will quit and all changes will be lost. Script - + স্ক্রিপ্ট @@ -1539,12 +1540,12 @@ The installer will quit and all changes will be lost. Set keyboard model to %1.<br/> - + %1 এ কীবোর্ড নকশা নির্ধারণ করুন। Set keyboard layout to %1/%2. - + %1/%2 এ কীবোর্ড বিন্যাস নির্ধারণ করুন। @@ -1552,7 +1553,7 @@ The installer will quit and all changes will be lost. Keyboard - + কীবোর্ড @@ -1560,7 +1561,7 @@ The installer will quit and all changes will be lost. Keyboard - + কীবোর্ড @@ -1568,17 +1569,17 @@ The installer will quit and all changes will be lost. System locale setting - + সিস্টেম লোকেল সেটিং The system locale setting affects the language and character set for some command line user interface elements.<br/>The current setting is <strong>%1</strong>. - + সিস্টেম স্থানীয় বিন্যাসন কিছু কমান্ড লাইন ব্যবহারকারী ইন্টারফেস উপাদানের জন্য ভাষা এবং অক্ষর সেট কে প্রভাবিত করে. <br/>বর্তমান বিন্যাসন <strong>%1</strong>। &Cancel - + এবংবাতিল করুন @@ -1591,7 +1592,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -1601,7 +1602,7 @@ The installer will quit and all changes will be lost. I accept the terms and conditions above. - + আমি উপরের শর্তাবলী মেনে নিচ্ছি। @@ -1634,7 +1635,7 @@ The installer will quit and all changes will be lost. License - + লাইসেন্স @@ -1702,18 +1703,18 @@ The installer will quit and all changes will be lost. Region: - + অঞ্চল: Zone: - + বলয়: &Change... - + এবংপরিবর্তন করুন... @@ -1721,7 +1722,7 @@ The installer will quit and all changes will be lost. Location - + অবস্থান @@ -1729,7 +1730,7 @@ The installer will quit and all changes will be lost. Location - + অবস্থান @@ -2200,7 +2201,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -2241,7 +2242,7 @@ The installer will quit and all changes will be lost. Name - + নাম @@ -2254,17 +2255,17 @@ The installer will quit and all changes will be lost. Form - + ফর্ম Keyboard Model: - + কীবোর্ড নকশা: Type here to test your keyboard - + আপনার কীবোর্ড পরীক্ষা করতে এখানে টাইপ করুন @@ -2272,12 +2273,12 @@ The installer will quit and all changes will be lost. Form - + ফর্ম What is your name? - + আপনার নাম কি? @@ -2287,7 +2288,7 @@ The installer will quit and all changes will be lost. What name do you want to use to log in? - + লগ-ইন করতে আপনি কোন নাম ব্যবহার করতে চান? @@ -2297,12 +2298,12 @@ The installer will quit and all changes will be lost. What is the name of this computer? - + এই কম্পিউটারের নাম কি? <small>This name will be used if you make the computer visible to others on a network.</small> - + <small>আপনি যদি কম্পিউটারটিকে অন্যদের কাছে একটি নেটওয়ার্কে দৃশ্যমান করেন তাহলে এই নামটি ব্যবহার করা হবে।</small> @@ -2312,13 +2313,13 @@ The installer will quit and all changes will be lost. Choose a password to keep your account safe. - + আপনার অ্যাকাউন্ট সুরক্ষিত রাখতে একটি পাসওয়ার্ড নির্বাচন করুন। <small>Enter the same password twice, so that it can be checked for typing errors. A good password will contain a mixture of letters, numbers and punctuation, should be at least eight characters long, and should be changed at regular intervals.</small> - + <small>একই পাসওয়ার্ড দুইবার প্রবেশ করান, যাতে এটি টাইপিং ত্রুটির জন্য পরীক্ষা করা যেতে পারে। একটি ভাল পাসওয়ার্ড অক্ষর, সংখ্যা এবং বিরামচিহ্নের একটি মিশ্রণ ধারণ করবে, অন্তত আট অক্ষর দীর্ঘ হওয়া উচিত, এবং নিয়মিত বিরতিতে পরিবর্তন করা উচিত।</small> @@ -2345,23 +2346,23 @@ The installer will quit and all changes will be lost. Log in automatically without asking for the password. - + পাসওয়ার্ড না চাইলে স্বয়ংক্রিয়ভাবে লগ ইন করুন। Use the same password for the administrator account. - + প্রশাসক হিসাবের জন্য একই গুপ্ত-সংকেত ব্যবহার করুন। Choose a password for the administrator account. - + প্রশাসক হিসাবের জন্য একটি পাসওয়ার্ড নির্বাচন করুন। <small>Enter the same password twice, so that it can be checked for typing errors.</small> - + <small>একই পাসওয়ার্ড দুইবার প্রবেশ করান, যাতে এটি টাইপিং ত্রুটির জন্য পরীক্ষা করা যেতে পারে।</small> @@ -2369,43 +2370,43 @@ The installer will quit and all changes will be lost. Root - + রুট Home - + বাড়ি Boot - + বুট EFI system - + ইএফআই সিস্টেম Swap - + অদলবদল New partition for %1 - + %1 এর জন্য নতুন পার্টিশন New partition - + নতুন পার্টিশন %1 %2 size[number] filesystem[name] - + %1 %2 @@ -2414,33 +2415,33 @@ The installer will quit and all changes will be lost. Free Space - + খালি জায়গা New partition - + নতুন পার্টিশন Name - + নাম File System - + নথি ব্যবস্থা Mount Point - + মাউন্ট পয়েন্ট Size - + আকার @@ -2448,22 +2449,22 @@ The installer will quit and all changes will be lost. Form - + ফর্ম Storage de&vice: - + স্টোরেজ ডিএবংভাইস &Revert All Changes - + এবংসকল পরিবর্তন ফিরিয়ে দিন New Partition &Table - + নতুন পার্টিশন এবংটেবিল @@ -2473,12 +2474,12 @@ The installer will quit and all changes will be lost. &Edit - + এবংসম্পাদনা করুন &Delete - + এবংমুছে ফেলুন @@ -2508,7 +2509,7 @@ The installer will quit and all changes will be lost. Are you sure you want to create a new partition table on %1? - + আপনি কি নিশ্চিত যে আপনি %1 এ একটি নতুন পার্টিশন টেবিল তৈরি করতে চান? @@ -2526,67 +2527,67 @@ The installer will quit and all changes will be lost. Gathering system information... - + সিস্টেম তথ্য সংগ্রহ করা হচ্ছে... Partitions - + পার্টিশনগুলো Install %1 <strong>alongside</strong> another operating system. - + অন্য অপারেটিং সিস্টেমের <strong>পাশাপাশি</strong> %1 ইনস্টল করুন। <strong>Erase</strong> disk and install %1. - + ডিস্ক <strong>মুছে ফেলুন</strong> এবং %1 সংস্থাপন করুন। <strong>Replace</strong> a partition with %1. - + %1 দিয়ে একটি পার্টিশন <strong>প্রতিস্থাপন করুন</strong>। <strong>Manual</strong> partitioning. - + <strong>ম্যানুয়াল</strong> পার্টিশনিং। Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). - + <strong>%2</strong> (%3) ডিস্কে অন্য অপারেটিং সিস্টেমের <strong>পাশাপাশি</strong> %1 ইনস্টল করুন। <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. - + ডিস্ক <strong>%2</strong> (%3) <strong>মুছে ফেলুন</strong> এবং %1 সংস্থাপন করুন। <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. - + %1 দিয়ে <strong>%2</strong> (%3) ডিস্কে একটি পার্টিশন <strong>প্রতিস্থাপন করুন</strong>। <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). - + <strong>%1</strong> (%2) ডিস্কে <strong>ম্যানুয়াল</strong> পার্টিশন করা হচ্ছে। Disk <strong>%1</strong> (%2) - + ডিস্ক <strong>%1</strong> (%2) Current: - + বর্তমান: After: - + পরে: @@ -2658,7 +2659,7 @@ The installer will quit and all changes will be lost. Form - + ফর্ম @@ -2768,22 +2769,22 @@ Output: %1 (%2) - + %1 (%2) unknown - + অজানা extended - + বর্ধিত unformatted - + অবিন্যাসিত @@ -2793,13 +2794,13 @@ Output: Default Keyboard Model - + পূর্বনির্ধারিত কীবোর্ডের নকশা Default - + পূর্বনির্ধারিত @@ -2837,7 +2838,7 @@ Output: Unpartitioned space or unknown partition table - + অবিভাজনকৃত স্থান বা অজানা পার্টিশন টেবিল @@ -2881,74 +2882,74 @@ Output: Form - + ফর্ম Select where to install %1.<br/><font color="red">Warning: </font>this will delete all files on the selected partition. - + %1 কোথায় সংস্থাপন করতে হবে তা নির্বাচন করুন।<br/><font color="red"> সতর্কীকরণ: </font>এটি নির্বাচিত পার্টিশনের সকল ফাইল মুছে ফেলবে। The selected item does not appear to be a valid partition. - + নির্বাচিত বিষয়োপকরণটি একটি বৈধ পার্টিশন বলে মনে হচ্ছে না। %1 cannot be installed on empty space. Please select an existing partition. - + %1 ফাঁকা স্থানে সংস্থাপন করা যাবে না। অনুগ্রহ করে একটি বিদ্যমান পার্টিশন নির্বাচন করুন। %1 cannot be installed on an extended partition. Please select an existing primary or logical partition. - + %1 একটি বর্ধিত পার্টিশনে সংস্থাপন করা যাবে না। অনুগ্রহ করে একটি বিদ্যমান প্রাথমিক বা যৌক্তিক বিভাজন নির্বাচন করুন। %1 cannot be installed on this partition. - + %1 এই পার্টিশনে সংস্থাপন করা যাবে না। Data partition (%1) - + ডাটা পার্টিশন (%1) Unknown system partition (%1) - + অজানা সিস্টেম পার্টিশন (%1) %1 system partition (%2) - + %1 সিস্টেম পার্টিশন (%2) <strong>%4</strong><br/><br/>The partition %1 is too small for %2. Please select a partition with capacity at least %3 GiB. - + <strong>%4</strong><br/><br/>%1 পার্টিশন %2 এর জন্য খুবই ছোট। অনুগ্রহ করে কমপক্ষে %3 GiB ধারণ ক্ষমতা সম্পন্ন একটি পার্টিশন নির্বাচন করুন। <strong>%2</strong><br/><br/>An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. - + <strong>%2</strong><br/><br/> একটি EFI সিস্টেম পার্টিশন এই সিস্টেমের কোথাও খুঁজে পাওয়া যাবে না। অনুগ্রহ করে ফিরে যান এবং %1 সেট আপ করতে ম্যানুয়াল পার্টিশনিং ব্যবহার করুন। <strong>%3</strong><br/><br/>%1 will be installed on %2.<br/><font color="red">Warning: </font>all data on partition %2 will be lost. - + <strong>%3</strong><br/><br/>%1 %2-এ ইনস্টল করা হবে।<br/><font color="red"> সতর্কীকরণ: </font>%2 পার্টিশনের সকল উপাত্ত হারিয়ে যাবে। The EFI system partition at %1 will be used for starting %2. - + %1 এ EFI সিস্টেম পার্টিশন %2 শুরু করার জন্য ব্যবহার করা হবে। EFI system partition: - + EFI সিস্টেম পার্টিশন: @@ -3040,7 +3041,7 @@ Output: Resize partition %1. - + পার্টিশন %1 পুনঃআকার করুন। @@ -3055,7 +3056,7 @@ Output: The installer failed to resize partition %1 on disk '%2'. - + ইনস্টলার '%2' ডিস্কে %1 পার্টিশন পুনঃআকার করতে ব্যর্থ হয়েছে। @@ -3131,12 +3132,12 @@ Output: Scanning storage devices... - + স্টোরেজ ডিভাইস স্ক্যান করা হচ্ছে... Partitioning - + পার্টিশন করা হচ্ছে @@ -3144,29 +3145,29 @@ Output: Set hostname %1 - + হোস্টের নাম %1 নির্ধারণ করুন Set hostname <strong>%1</strong>. - + হোস্টনাম <strong>%1</strong> নির্ধারণ করুন। Setting hostname %1. - + হোস্টনাম %1 নির্ধারণ করা হচ্ছে। Internal Error - + অভ্যন্তরীণ ত্রুটি Cannot write hostname to target system - + লক্ষ্য ব্যবস্থায় হোস্টের নাম লেখা যাচ্ছে না @@ -3174,24 +3175,24 @@ Output: Set keyboard model to %1, layout to %2-%3 - + কীবোর্ড মডেলটিকে %1, লেআউটটিকে %2-%3 এ সেট করুন Failed to write keyboard configuration for the virtual console. - + ভার্চুয়াল কনসোলের জন্য কীবোর্ড কনফিগারেশন লিখতে ব্যর্থ হয়েছে। Failed to write to %1 - + %1 এ লিখতে ব্যর্থ Failed to write keyboard configuration for X11. - + X11 এর জন্য কীবোর্ড কনফিগারেশন লিখতে ব্যর্থ হয়েছে। @@ -3287,22 +3288,22 @@ Output: Set password for user %1 - + ব্যবহারকারীর জন্য গুপ্ত-সংকেত নির্ধারণ করুন % 1 Setting password for user %1. - + ব্যবহারকারীর %1-এর জন্য গুপ্ত-সংকেত নির্ধারণ করা হচ্ছে। Bad destination system path. - + খারাপ গন্তব্য সিস্টেম পথ। rootMountPoint is %1 - + রুটমাউন্টপয়েন্টটি % 1 @@ -3317,12 +3318,12 @@ Output: Cannot set password for user %1. - + % 1 ব্যবহারকারীর জন্য পাসওয়ার্ড নির্ধারণ করা যাচ্ছে না। usermod terminated with error code %1. - + %1 ত্রুটি কোড দিয়ে ব্যবহারকারীমোড সমাপ্ত করা হয়েছে। @@ -3330,37 +3331,37 @@ Output: Set timezone to %1/%2 - + %1/%2 এ সময়অঞ্চল নির্ধারণ করুন Cannot access selected timezone path. - + নির্বাচিত সময়অঞ্চল পথে প্রবেশ করতে পারে না। Bad path: %1 - + খারাপ পথ: %1 Cannot set timezone. - + সময়অঞ্চল নির্ধারণ করা যাচ্ছে না। Link creation failed, target: %1; link name: %2 - + লিংক তৈরি ব্যর্থ হয়েছে, লক্ষ্য: %1; লিংকের নাম: %2 Cannot set timezone, - + সময়অঞ্চল নির্ধারণ করা যাচ্ছে না, Cannot open /etc/timezone for writing - + লেখার জন্য /ইত্যাদি/সময়অঞ্চল খোলা যাচ্ছে না @@ -3390,7 +3391,7 @@ Output: This is an overview of what will happen once you start the install procedure. - + আপনি ইনস্টল প্রক্রিয়া শুরু করার পর কি হবে তার একটি পর্যালোচনা। @@ -3398,7 +3399,7 @@ Output: Summary - + সারাংশ @@ -3487,7 +3488,7 @@ Output: Form - + ফর্ম @@ -3548,7 +3549,7 @@ Output: Your passwords do not match! - + আপনার পাসওয়ার্ড মেলে না! @@ -3556,7 +3557,7 @@ Output: Users - + ব্যবহারকারীরা @@ -3630,7 +3631,7 @@ Output: Form - + ফর্ম @@ -3641,7 +3642,7 @@ Output: &About - + এবংসম্পর্কে @@ -3661,7 +3662,7 @@ Output: &Support - + এবংসহায়তা @@ -3671,7 +3672,7 @@ Output: &Known issues - + এবংপরিচিত বিষয়গুলো @@ -3681,7 +3682,7 @@ Output: &Release notes - + এবংনোট প্রকাশ করুন @@ -3701,12 +3702,12 @@ Output: <h1>Welcome to the %1 installer.</h1> - + <h1>%1 ইনস্টলারে স্বাগতম।</h1> %1 support - + %1 সহায়তা @@ -3716,7 +3717,7 @@ Output: About %1 installer - + %1 ইনস্টলার সম্পর্কে @@ -3729,7 +3730,7 @@ Output: Welcome - + স্বাগতম @@ -3737,7 +3738,7 @@ Output: Welcome - + স্বাগতম diff --git a/lang/calamares_da.ts b/lang/calamares_da.ts index 31f66bd32..7e6340a46 100644 --- a/lang/calamares_da.ts +++ b/lang/calamares_da.ts @@ -717,7 +717,7 @@ Installationsprogrammet vil stoppe og alle ændringer vil gå tabt. Set timezone to %1/%2. - + Indstil tidszone til %1/%2. @@ -802,7 +802,7 @@ Installationsprogrammet vil stoppe og alle ændringer vil gå tabt. '%1' is not allowed as username. - + '%1' er ikke tilladt som brugernavn. @@ -827,7 +827,7 @@ Installationsprogrammet vil stoppe og alle ændringer vil gå tabt. '%1' is not allowed as hostname. - + '%1' er ikke tilladt som værtsnavn. @@ -3801,7 +3801,8 @@ setting <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Lokaliteter</h1> </br> + Systemets lokalitetsindstillinger påvirker tal- og datoformater. Den nuværende indstilling er <strong>%1</strong>. diff --git a/lang/calamares_hi.ts b/lang/calamares_hi.ts index c651eaea9..b1d7b3ce8 100644 --- a/lang/calamares_hi.ts +++ b/lang/calamares_hi.ts @@ -530,7 +530,7 @@ The installer will quit and all changes will be lost. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - <strong>मैनुअल विभाजन</strong><br/> आप स्वयं भी विभाजन बना व उनका आकार बदल सकते है। + <strong>मैनुअल विभाजन</strong><br/> स्वयं विभाजन बनाएँ या उनका आकार बदलें। @@ -717,7 +717,7 @@ The installer will quit and all changes will be lost. Set timezone to %1/%2. - + समय क्षेत्र %1%2 सेट करें। @@ -797,42 +797,42 @@ The installer will quit and all changes will be lost. Your username is too long. - आपका उपयोक्ता नाम बहुत लंबा है। + उपयोक्ता नाम बहुत लंबा है। '%1' is not allowed as username. - + उपयोक्ता नाम के रूप में '%1' का उपयोग अस्वीकार्य है। Your username must start with a lowercase letter or underscore. - आपके उपयोक्ता नाम का आरंभ lowercase अक्षर या अंडरस्कोर(_) से ही होना चाहिए। + उपयोक्ता नाम का आरंभ केवल लोअरकेस अक्षर या अंडरस्कोर(-) से ही करें। Only lowercase letters, numbers, underscore and hyphen are allowed. - केवल lowercase अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) का उपयोग ही मान्य है। + केवल लोअरकेस अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) ही स्वीकार्य हैं। Your hostname is too short. - आपका होस्ट नाम बहुत छोटा है। + होस्ट नाम बहुत छोटा है। Your hostname is too long. - आपका होस्ट नाम बहुत लंबा है। + होस्ट नाम बहुत लंबा है। '%1' is not allowed as hostname. - + होस्ट नाम के रूप में '%1' का उपयोग अस्वीकार्य है। Only letters, numbers, underscore and hyphen are allowed. - केवल अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) का उपयोग ही मान्य है। + केवल अक्षर, अंक, अंडरस्कोर(_) व हाइफ़न(-) ही स्वीकार्य हैं। @@ -3799,7 +3799,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>स्थानिकी</h1> </br> + सिस्टम स्थानिकी सेटिंग संख्या व दिनांक के प्रारूप को प्रभावित करती है। वर्तमान सेटिंग <strong>%1</strong> है। diff --git a/lang/calamares_sv.ts b/lang/calamares_sv.ts index 4c6a523e6..c1bc83bc7 100644 --- a/lang/calamares_sv.ts +++ b/lang/calamares_sv.ts @@ -137,7 +137,7 @@ Programmed job failure was explicitly requested. - + Programmerat jobbfel begärdes uttryckligen. @@ -222,7 +222,7 @@ Loading failed. - + Laddning misslyckades. @@ -716,7 +716,7 @@ Alla ändringar kommer att gå förlorade. Set timezone to %1/%2. - + Sätt tidszon till %1/%2. @@ -776,22 +776,22 @@ Alla ändringar kommer att gå förlorade. <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Välkommen till Calamares installationsprogram för %1</h1> <h1>Welcome to %1 setup</h1> - + <h1>Välkommen till %1 installation</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>Välkommen till Calamares installationsprogram för %1</h1> <h1>Welcome to the %1 installer</h1> - + <h1>Välkommen till %1-installeraren</h1> @@ -801,7 +801,7 @@ Alla ändringar kommer att gå förlorade. '%1' is not allowed as username. - + '%1' är inte tillåtet som användarnamn. @@ -826,7 +826,7 @@ Alla ändringar kommer att gå förlorade. '%1' is not allowed as hostname. - + '%1' är inte tillåtet som värdnamn. @@ -839,7 +839,7 @@ Alla ändringar kommer att gå förlorade. Contextual Processes Job - + Kontextuellt processjobb @@ -1476,7 +1476,7 @@ Alla ändringar kommer att gå förlorade. OEM Batch Identifier - + OEM-batchidentifierare @@ -1795,7 +1795,10 @@ Alla ändringar kommer att gå förlorade. Please select your preferred location on the map so the installer can suggest the locale and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming. - + Snälla välj din föredragna plats på kartan så installationsprogrammet kan föreslå nationella inställningar + och tidszons inställningar åt dig. Du kan finjustera de föreslagna inställningarna nedan. +Sök på kartan genom att dra + för att flytta och använd +/- knapparna för att zooma in/ut eller så använder du musens scrollhjul för att zooma. @@ -1910,12 +1913,12 @@ Alla ändringar kommer att gå förlorade. Ba&tch: - + Gr&upp: <html><head/><body><p>Enter a batch-identifier here. This will be stored in the target system.</p></body></html> - + <html><head/><body><p>Ange en batch-identifierare här. Den kommer lagras på målsystemet.</p></body></html> @@ -1933,7 +1936,7 @@ Alla ändringar kommer att gå förlorade. Set the OEM Batch Identifier to <code>%1</code>. - + Sätt OEM-batchidentifierare till <code>%1</code>. @@ -1946,7 +1949,7 @@ Alla ändringar kommer att gå förlorade. To be able to select a timezone, make sure you are connected to the internet. Restart the installer after connecting. You can fine-tune Language and Locale settings below. - + För att kunna välja en tidszon, se till att du är ansluten till internet. Starta om installationsprogrammet efter anslutningen. Du kan finjustera språk och nationella inställningar nedan. @@ -1969,7 +1972,7 @@ Alla ändringar kommer att gå förlorade. Memory allocation error when setting '%1' - + Minnesallokerings fel då '%1' skulle ställas in @@ -2144,7 +2147,7 @@ Alla ändringar kommer att gå förlorade. Bad integer value of setting - %1 - + Dåligt heltals värde på inställning - %1 @@ -2851,7 +2854,8 @@ Utdata: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Denna dator uppfyller inte alla rekommenderade krav för att installera %1. +<br/>Installationen kan fortsätta, men alla alternativ och funktioner kanske inte kan användas.</p> @@ -2962,13 +2966,15 @@ Utdata: <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + <p>Denna dator uppfyller inte minimikraven för att installera %1.<br/> +Installationen kan inte fortsätta.</p> <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Denna dator uppfyller inte alla rekommenderade krav för att installera %1. +<br/>Installationen kan fortsätta, men alla alternativ och funktioner kanske inte kan användas.</p> @@ -3411,17 +3417,17 @@ Utdata: Installation feedback - + Installationsåterkoppling Sending installation feedback. - + Skickar installationsåterkoppling Internal error in install-tracking. - + Internt fel i install-tracking. @@ -3434,28 +3440,28 @@ Utdata: KDE user feedback - + KDE användarfeedback Configuring KDE user feedback. - + Konfigurerar KDE användarfeedback. Error in KDE user feedback configuration. - + Fel vid konfigurering av KDE användarfeedback. Could not configure KDE user feedback correctly, script error %1. - + Kunde inte konfigurera KDE användarfeedback korrekt, script fel %1. Could not configure KDE user feedback correctly, Calamares error %1. - + Kunde inte konfigurera KDE användarfeedback korrekt, Calamares fel %1. @@ -3474,7 +3480,7 @@ Utdata: Error in machine feedback configuration. - + Fel vid konfigurering av maskin feedback @@ -3502,32 +3508,32 @@ Utdata: <html><head/><body><p>Click here to send <span style=" font-weight:600;">no information at all</span> about your installation.</p></body></html> - + <html><head/><body><p>Klicka här för att inte <span style=" font-weight:600;"> skicka någon information alls om din installation.</p></body></html> <html><head/><body><p><a href="placeholder"><span style=" text-decoration: underline; color:#2980b9;">Click here for more information about user feedback</span></a></p></body></html> - + <html><head/><body><p><a href="placeholder"><span style=" text-decoration: underline; color:#2980b9;"> Klicka här för information om användarfeedback </span></a></p></body></html> Tracking helps %1 to see how often it is installed, what hardware it is installed on and which applications are used. To see what will be sent, please click the help icon next to each area. - + Spårning hjälper %1 att se hur ofta den är installerad, vilken hårdvara den är installerad på och vilka program som används. För att se vad som skickas, Klicka på hjälp ikonen vad sidan av varje område för att se vad som skickas. By selecting this you will send information about your installation and hardware. This information will only be sent <b>once</b> after the installation finishes. - + Genom att välja detta, kommer du skicka information om din installation och hårdvara. Denna information kommer <b>enbart skickas en gång</b> efter att installationen slutförts. By selecting this you will periodically send information about your <b>machine</b> installation, hardware and applications, to %1. - + Genom att välja detta, kommer du periodiskt skicka information om din <b>maskin</b>installation, hårdvara och program, till %1 By selecting this you will regularly send information about your <b>user</b> installation, hardware, applications and application usage patterns, to %1. - + Genom att välja detta, kommer du regelbundet skicka information om din <b>användar</b>installation, hårdvara, program och dina program användningsmönster till %1. @@ -3786,13 +3792,15 @@ Utdata: <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>. - + <h1>Språk</h1> </br> +Systemspråket påverkar vilket språk och teckenuppsättning somliga kommandoradsprogram använder. Den nuvarande inställningen är <strong>%1</strong>. <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Nationella inställningar</h1> </br> +Systems nationella inställningar påverkar nummer och datumformat. Den nuvarande inställningen är <strong>%3</strong>. From b8530c2c08de61d303bd8a06721a8f6bae5bc89b Mon Sep 17 00:00:00 2001 From: Calamares CI Date: Wed, 5 Aug 2020 13:53:54 +0200 Subject: [PATCH 20/91] i18n: [python] Automatic merge of Transifex translations --- lang/python/ko/LC_MESSAGES/python.mo | Bin 8279 -> 8279 bytes lang/python/ko/LC_MESSAGES/python.po | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/python/ko/LC_MESSAGES/python.mo b/lang/python/ko/LC_MESSAGES/python.mo index 22a53a6884aa932a0e905258669100654da65cbb..b15abd450a8deb0799083f3f81881fec92d42570 100644 GIT binary patch delta 24 fcmccaaNS`;j3BpHX, 2018 -# MarongHappy , 2020 +# JungHee Lee , 2020 # #, fuzzy msgid "" @@ -14,7 +14,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-07-29 11:03+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: MarongHappy , 2020\n" +"Last-Translator: JungHee Lee , 2020\n" "Language-Team: Korean (https://www.transifex.com/calamares/teams/20061/ko/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" From 272cf099becd232cc72b105497eea2b92f73e7a1 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 14:07:39 +0200 Subject: [PATCH 21/91] [hostinfo] Try to recognize ARM as well - /proc/cpuinfo is a terrible information source; it contains very different information on x86 from arm (testen on rpi4 and rock64). --- src/modules/hostinfo/HostInfoJob.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/modules/hostinfo/HostInfoJob.cpp b/src/modules/hostinfo/HostInfoJob.cpp index c2959fb6b..1d4676f65 100644 --- a/src/modules/hostinfo/HostInfoJob.cpp +++ b/src/modules/hostinfo/HostInfoJob.cpp @@ -91,6 +91,17 @@ hostCPUmatch( const QString& s ) return QString(); } +static QString +hostCPUmatchARM( const QString& s ) +{ + // Both Rock64 and Raspberry pi mention 0x41 + if ( s.contains( ": 0x41" ) ) + { + return QStringLiteral( "ARM" ); + } + return QString(); +} + #if defined( Q_OS_FREEBSD ) QString hostCPU_FreeBSD() @@ -127,6 +138,10 @@ hostCPU_Linux() { return hostCPUmatch( line ); } + if ( line.startsWith( "CPU implementer" ) ) + { + return hostCPUmatchARM( line ); + } } } return QString(); // Not open, or not found From 082770032f6262014920e7c0392dd621df330c95 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 14:24:28 +0200 Subject: [PATCH 22/91] [hostinfo] Massage test to handle ARM - there **is** another source of information about the CPU, so in the test use that to cross-check what hostCPU() says. NB: it's probably a good idea to fall back on the same file in hostCPU() for better accuracy. --- src/modules/hostinfo/Tests.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/modules/hostinfo/Tests.cpp b/src/modules/hostinfo/Tests.cpp index 8182da365..7ab797ed4 100644 --- a/src/modules/hostinfo/Tests.cpp +++ b/src/modules/hostinfo/Tests.cpp @@ -63,8 +63,29 @@ HostInfoTests::testHostOS() // This is a lousy test, too: the implementation reads /proc/cpuinfo // and that's the only way we could use, too, to find what the "right" // answer is. - QStringList cpunames{ QStringLiteral( "Intel" ), QStringLiteral( "AMD" ) }; - QVERIFY( cpunames.contains( hostCPU() ) ); + QStringList x86cpunames{ QStringLiteral( "Intel" ), QStringLiteral( "AMD" ) }; + QStringList armcpunames{ QStringLiteral( "ARM" ) }; + const QString cpu = hostCPU(); + QVERIFY( x86cpunames.contains( cpu ) || armcpunames.contains( cpu ) ); + + // Try to detect family in a different way + QFile modalias( "/sys/devices/system/cpu/modalias" ); + if ( modalias.open( QIODevice::ReadOnly ) ) + { + QString cpumodalias = modalias.readLine(); + if ( cpumodalias.contains( "type:x86" ) ) + { + QVERIFY( x86cpunames.contains( cpu ) ); + } + else if ( cpumodalias.contains( "type:aarch64" ) ) + { + QVERIFY( armcpunames.contains( cpu ) ); + } + else + { + QCOMPARE( cpu, QString( "Unknown CPU modalias '%1'" ).arg(cpumodalias) ); + } + } } From fce05acf1efd696dfaf0207a539a39a6cc6e3843 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 15:11:52 +0200 Subject: [PATCH 23/91] [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 | 242 ++++----------------------- src/libcalamares/locale/TimeZone.h | 166 ++---------------- 3 files changed, 38 insertions(+), 408 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() ) + +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 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 +/** @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 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 From ca40d2e2d9134797cdf3ad909578382f79c38635 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 15:17:09 +0200 Subject: [PATCH 24/91] [libcalamares] Introduce a failing test for the number of regions --- src/libcalamares/locale/Tests.cpp | 12 ++++++++++-- src/libcalamares/locale/TimeZone.cpp | 23 ++++++++++++----------- src/libcalamares/locale/TimeZone.h | 5 ++--- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 16b7d11a7..54e6cd848 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -47,6 +47,7 @@ private Q_SLOTS: void testInterlingue(); // TimeZone testing + void testRegions(); void testSimpleZones(); void testComplexZones(); }; @@ -244,18 +245,25 @@ LocaleTests::testTranslatableConfig2() QCOMPARE( ts3.count(), 1 ); // The empty string } +void +LocaleTests::testRegions() +{ + CalamaresUtils::Locale::RegionsModel regions; + + QVERIFY( regions.rowCount( QModelIndex() ) > 3 ); // Africa, America, Asia +} + + void LocaleTests::testSimpleZones() { using namespace CalamaresUtils::Locale; - } void LocaleTests::testComplexZones() { using namespace CalamaresUtils::Locale; - } QTEST_GUILESS_MAIN( LocaleTests ) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 37f94b6f2..0dba52a2a 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -92,36 +92,37 @@ namespace CalamaresUtils namespace Locale { -struct Private { +struct Private +{ }; -static Private* privateInstance() +static Private* +privateInstance() { static Private* s_p = new Private; return s_p; } -RegionsModel::RegionsModel() -: QAbstractListModel() -, m_private( privateInstance() ) +RegionsModel::RegionsModel( QObject* parent ) + : QAbstractListModel( parent ) + , m_private( privateInstance() ) { } -RegionsModel::~RegionsModel() -{ -} +RegionsModel::~RegionsModel() {} -int RegionsModel::rowCount(const QModelIndex& parent) const +int +RegionsModel::rowCount( const QModelIndex& parent ) const { return 0; } -QVariant RegionsModel::data(const QModelIndex& index, int role) const +QVariant +RegionsModel::data( const QModelIndex& index, int role ) const { return QVariant(); } - } // namespace Locale } // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index a34e03248..b3d57c4fd 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -43,17 +43,16 @@ class DLLEXPORT RegionsModel : public QAbstractListModel { Q_OBJECT - RegionsModel(); public: + RegionsModel( QObject* parent = nullptr ); virtual ~RegionsModel() override; - static RegionsModel* instance(); int rowCount( const QModelIndex& parent ) const override; QVariant data( const QModelIndex& index, int role ) const override; private: - Private *m_private; + Private* m_private; }; } // namespace Locale From 82cc652f55200ac3b0a06fa7517bbb867604a3cb Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 16:21:05 +0200 Subject: [PATCH 25/91] [libcalamares] Re-done zones loading - just make one big list of zones, one short list of regions - the models are non-functional right now --- src/libcalamares/locale/Tests.cpp | 4 +- src/libcalamares/locale/TimeZone.cpp | 232 ++++++++++++++++++++++++++- src/libcalamares/locale/TimeZone.h | 16 ++ 3 files changed, 250 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 54e6cd848..4690a50a3 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -257,7 +257,9 @@ LocaleTests::testRegions() void LocaleTests::testSimpleZones() { - using namespace CalamaresUtils::Locale; + CalamaresUtils::Locale::ZonesModel zones; + + QVERIFY( zones.rowCount( QModelIndex() ) > 24 ); } void diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 0dba52a2a..e12f404a5 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -23,6 +23,10 @@ #include "TimeZone.h" #include "utils/Logger.h" +#include "utils/String.h" + +#include +#include static const char TZ_DATA_FILE[] = "/usr/share/zoneinfo/zone.tab"; @@ -59,6 +63,42 @@ getRightGeoLocation( QString str ) return sign * num; } +/** @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 the human-readable part + * may need to be translated through tr(), and that takes a char*. + * 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 ); + explicit CStringPair( const QString& s ); + 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; } + bool operator<( const CStringPair& other ) const { return m_key < other.m_key; } + +protected: + char* m_human = nullptr; + QString m_key; +}; + /** @brief Massage an identifier into a human-readable form * * Makes a copy of @p s, caller must free() it. @@ -86,6 +126,162 @@ munge( const char* s ) return t; } +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 ) +{ +} + +CStringPair::CStringPair( const char* s1 ) + : m_human( s1 ? munge( s1 ) : nullptr ) + , m_key( s1 ? QString( s1 ) : QString() ) +{ +} + +CStringPair::CStringPair( const QString& s ) + : m_human( strdup( s.toUtf8().constData() ) ) + , m_key( s ) +{ +} + + +CStringPair::~CStringPair() +{ + free( m_human ); +} + + +class TimeZoneData : public CStringPair +{ +public: + TimeZoneData( const QString& region, + const QString& zone, + const QString& country, + double latitude, + double longitude ); + QString tr() const override; + +private: + QString m_region; + QString m_country; + double m_latitude; + double m_longitude; +}; + +TimeZoneData::TimeZoneData( const QString& region, + const QString& zone, + const QString& country, + double latitude, + double longitude ) + : CStringPair( zone ) + , m_region( region ) + , m_country( country ) + , m_latitude( latitude ) + , m_longitude( longitude ) +{ +} + +QString +TimeZoneData::tr() const +{ + // NOTE: context name must match what's used in zone-extractor.py + return QObject::tr( m_human, "tz_names" ); +} + + +class RegionData : public CStringPair +{ +public: + using CStringPair::CStringPair; + QString tr() const override; +}; + +QString +RegionData::tr() const +{ + // NOTE: context name must match what's used in zone-extractor.py + return QObject::tr( m_human, "tz_regions" ); +} + +static void +loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData >& zones ) +{ + QFile file( TZ_DATA_FILE ); + if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + 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; + } + + QString countryCode = list.at( 0 ).trimmed(); + if ( countryCode.size() != 2 ) + { + continue; + } + + timezoneParts.removeFirst(); + QString zone = timezoneParts.join( '/' ); + if ( zone.length() < 2 ) + { + continue; + } + + QString position = list.at( 1 ); + int cooSplitPos = position.indexOf( QRegExp( "[-+]" ), 1 ); + double latitude; + double longitude; + if ( cooSplitPos > 0 ) + { + latitude = getRightGeoLocation( position.mid( 0, cooSplitPos ) ); + longitude = getRightGeoLocation( position.mid( cooSplitPos ) ); + } + else + { + continue; + } + + // Now we have region, zone, country, lat and longitude + RegionData r( region ); + if ( regions.indexOf( r ) < 0 ) + { + regions.append( std::move( r ) ); + } + zones.append( TimeZoneData( region, zone, countryCode, latitude, longitude ) ); + } + } +} namespace CalamaresUtils { @@ -94,6 +290,16 @@ namespace Locale struct Private { + QVector< RegionData > m_regions; + QVector< TimeZoneData > m_zones; + + Private() + { + m_regions.reserve( 12 ); // reasonable guess + m_zones.reserve( 452 ); // wc -l /usr/share/zoneinfo/zone.tab + + loadTZData( m_regions, m_zones ); + } }; static Private* @@ -114,7 +320,7 @@ RegionsModel::~RegionsModel() {} int RegionsModel::rowCount( const QModelIndex& parent ) const { - return 0; + return m_private->m_regions.count(); } QVariant @@ -123,6 +329,30 @@ RegionsModel::data( const QModelIndex& index, int role ) const return QVariant(); } +ZonesModel::ZonesModel( QObject* parent ) + : QAbstractListModel( parent ) + , m_private( privateInstance() ) +{ +} + +ZonesModel::~ZonesModel() {} + +int +ZonesModel::rowCount( const QModelIndex& parent ) const +{ + return m_private->m_zones.count(); +} + +QVariant +ZonesModel::data( const QModelIndex& index, int role ) const +{ + return QVariant(); +} + } // namespace Locale } // namespace CalamaresUtils + +#include "utils/moc-warnings.h" + +#include "TimeZone.moc" diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index b3d57c4fd..4ded57d8f 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -55,6 +55,22 @@ private: Private* m_private; }; +class DLLEXPORT ZonesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + ZonesModel( QObject* parent = nullptr ); + virtual ~ZonesModel() override; + + int rowCount( const QModelIndex& parent ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + +private: + Private* m_private; +}; + + } // namespace Locale } // namespace CalamaresUtils From 609ea8350c947ad492e0a0a4995a3519b695f4da Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 16:25:45 +0200 Subject: [PATCH 26/91] [libcalamares] Failing test: there is data in the regions model --- src/libcalamares/locale/Tests.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 4690a50a3..5ac70d3d0 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -251,6 +251,17 @@ LocaleTests::testRegions() CalamaresUtils::Locale::RegionsModel regions; QVERIFY( regions.rowCount( QModelIndex() ) > 3 ); // Africa, America, Asia + + QStringList names; + for ( int i = 0; i < regions.rowCount( QModelIndex() ); ++i ) + { + QVariant name = regions.data( regions.index( i ), Qt::UserRole ); + QVERIFY( name.isValid() ); + QVERIFY( !name.toString().isEmpty() ); + names.append( name.toString() ); + } + + QVERIFY( names.contains( "America" ) ); } From 33e39b92fb9f623256f4ead751b53d058284e59b Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 16:36:00 +0200 Subject: [PATCH 27/91] [libcalamares] Satisfy test, return region names --- src/libcalamares/locale/Tests.cpp | 3 ++- src/libcalamares/locale/TimeZone.cpp | 20 ++++++++++++++++++++ src/libcalamares/locale/TimeZone.h | 8 +++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 5ac70d3d0..e41e3a30b 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -255,13 +255,14 @@ LocaleTests::testRegions() QStringList names; for ( int i = 0; i < regions.rowCount( QModelIndex() ); ++i ) { - QVariant name = regions.data( regions.index( i ), Qt::UserRole ); + QVariant name = regions.data( regions.index( i ), Qt::DisplayRole ); QVERIFY( name.isValid() ); QVERIFY( !name.toString().isEmpty() ); names.append( name.toString() ); } QVERIFY( names.contains( "America" ) ); + QVERIFY( !names.contains( "UTC" ) ); } diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index e12f404a5..9f356d958 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -326,9 +326,29 @@ RegionsModel::rowCount( const QModelIndex& parent ) const QVariant RegionsModel::data( const QModelIndex& index, int role ) const { + if ( !index.isValid() || index.row() < 0 || index.row() >= m_private->m_regions.count() ) + { + return QVariant(); + } + + if ( role == Qt::DisplayRole ) + { + return m_private->m_regions[ index.row() ].tr(); + } + if ( role == KeyRole ) + { + return m_private->m_regions[ index.row() ].key(); + } return QVariant(); } +QHash< int, QByteArray > +RegionsModel::roleNames() const +{ + return { { Qt::DisplayRole, "name" }, { KeyRole, "key" } }; +} + + ZonesModel::ZonesModel( QObject* parent ) : QAbstractListModel( parent ) , m_private( privateInstance() ) diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 4ded57d8f..0a5fbf7f6 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -43,14 +43,20 @@ class DLLEXPORT RegionsModel : public QAbstractListModel { Q_OBJECT - public: + enum Roles + { + KeyRole = Qt::UserRole + 1 + }; + RegionsModel( QObject* parent = nullptr ); virtual ~RegionsModel() override; int rowCount( const QModelIndex& parent ) const override; QVariant data( const QModelIndex& index, int role ) const override; + QHash< int, QByteArray > roleNames() const override; + private: Private* m_private; }; From 1afdcc9c822a75152fca46cf982f936990849a06 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 16:45:41 +0200 Subject: [PATCH 28/91] [libcalamares] Give zones data, too - while here, fix bug in TimeZoneData that didn't munge names (so it reported "New_York") --- src/libcalamares/locale/Tests.cpp | 32 +++++++++++++++++++++++++--- src/libcalamares/locale/TimeZone.cpp | 31 ++++++++++++++++++++++----- src/libcalamares/locale/TimeZone.h | 9 ++++++++ 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index e41e3a30b..c660ccbfb 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -248,14 +248,15 @@ LocaleTests::testTranslatableConfig2() void LocaleTests::testRegions() { - CalamaresUtils::Locale::RegionsModel regions; + using namespace CalamaresUtils::Locale; + RegionsModel regions; QVERIFY( regions.rowCount( QModelIndex() ) > 3 ); // Africa, America, Asia QStringList names; for ( int i = 0; i < regions.rowCount( QModelIndex() ); ++i ) { - QVariant name = regions.data( regions.index( i ), Qt::DisplayRole ); + QVariant name = regions.data( regions.index( i ), RegionsModel::NameRole ); QVERIFY( name.isValid() ); QVERIFY( !name.toString().isEmpty() ); names.append( name.toString() ); @@ -269,9 +270,34 @@ LocaleTests::testRegions() void LocaleTests::testSimpleZones() { - CalamaresUtils::Locale::ZonesModel zones; + using namespace CalamaresUtils::Locale; + ZonesModel zones; QVERIFY( zones.rowCount( QModelIndex() ) > 24 ); + + QStringList names; + for ( int i = 0; i < zones.rowCount( QModelIndex() ); ++i ) + { + QVariant name = zones.data( zones.index( i ), ZonesModel::NameRole ); + QVERIFY( name.isValid() ); + QVERIFY( !name.toString().isEmpty() ); + names.append( name.toString() ); + } + + QVERIFY( names.contains( "Amsterdam" ) ); + if ( !names.contains( "New York" ) ) + { + for ( const auto& s : names ) + { + if ( s.startsWith( 'N' ) ) + { + cDebug() << s; + } + } + } + QVERIFY( names.contains( "New York" ) ); + QVERIFY( !names.contains( "America" ) ); + QVERIFY( !names.contains( "New_York" ) ); } void diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 9f356d958..05c082738 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -148,7 +148,7 @@ CStringPair::CStringPair( const char* s1 ) } CStringPair::CStringPair( const QString& s ) - : m_human( strdup( s.toUtf8().constData() ) ) + : m_human( munge( s.toUtf8().constData() ) ) , m_key( s ) { } @@ -331,13 +331,14 @@ RegionsModel::data( const QModelIndex& index, int role ) const return QVariant(); } - if ( role == Qt::DisplayRole ) + const auto& region = m_private->m_regions[ index.row() ]; + if ( role == NameRole ) { - return m_private->m_regions[ index.row() ].tr(); + return region.tr(); } if ( role == KeyRole ) { - return m_private->m_regions[ index.row() ].key(); + return region.key(); } return QVariant(); } @@ -345,7 +346,7 @@ RegionsModel::data( const QModelIndex& index, int role ) const QHash< int, QByteArray > RegionsModel::roleNames() const { - return { { Qt::DisplayRole, "name" }, { KeyRole, "key" } }; + return { { NameRole, "name" }, { KeyRole, "key" } }; } @@ -366,9 +367,29 @@ ZonesModel::rowCount( const QModelIndex& parent ) const QVariant ZonesModel::data( const QModelIndex& index, int role ) const { + if ( !index.isValid() || index.row() < 0 || index.row() >= m_private->m_zones.count() ) + { + return QVariant(); + } + + const auto& zone = m_private->m_zones[ index.row() ]; + if ( role == NameRole ) + { + return zone.tr(); + } + if ( role == KeyRole ) + { + return zone.key(); + } return QVariant(); } +QHash< int, QByteArray > +ZonesModel::roleNames() const +{ + return { { NameRole, "name" }, { KeyRole, "key" } }; +} + } // namespace Locale } // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 0a5fbf7f6..d87a5a57a 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -46,6 +46,7 @@ class DLLEXPORT RegionsModel : public QAbstractListModel public: enum Roles { + NameRole = Qt::DisplayRole, KeyRole = Qt::UserRole + 1 }; @@ -66,12 +67,20 @@ class DLLEXPORT ZonesModel : public QAbstractListModel Q_OBJECT public: + enum Roles + { + NameRole = Qt::DisplayRole, + KeyRole = Qt::UserRole + 1 + }; + ZonesModel( QObject* parent = nullptr ); virtual ~ZonesModel() override; int rowCount( const QModelIndex& parent ) const override; QVariant data( const QModelIndex& index, int role ) const override; + QHash< int, QByteArray > roleNames() const override; + private: Private* m_private; }; From 3e32335511a8989fe78abe986d02d6005b23b8b8 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 17:14:13 +0200 Subject: [PATCH 29/91] [libcalamares] Introduce a filtering model per-region --- src/libcalamares/locale/Tests.cpp | 55 ++++++++++++++++++++++++---- src/libcalamares/locale/TimeZone.cpp | 42 +++++++++++++++++++-- src/libcalamares/locale/TimeZone.h | 25 +++++++++++++ 3 files changed, 111 insertions(+), 11 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index c660ccbfb..8a6a6ad84 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -267,6 +267,19 @@ LocaleTests::testRegions() } +static void +displayedNames( QAbstractItemModel& model, QStringList& names ) +{ + names.clear(); + for ( int i = 0; i < model.rowCount( QModelIndex() ); ++i ) + { + QVariant name = model.data( model.index( i, 0 ), Qt::DisplayRole ); + QVERIFY( name.isValid() ); + QVERIFY( !name.toString().isEmpty() ); + names.append( name.toString() ); + } +} + void LocaleTests::testSimpleZones() { @@ -276,14 +289,7 @@ LocaleTests::testSimpleZones() QVERIFY( zones.rowCount( QModelIndex() ) > 24 ); QStringList names; - for ( int i = 0; i < zones.rowCount( QModelIndex() ); ++i ) - { - QVariant name = zones.data( zones.index( i ), ZonesModel::NameRole ); - QVERIFY( name.isValid() ); - QVERIFY( !name.toString().isEmpty() ); - names.append( name.toString() ); - } - + displayedNames( zones, names ); QVERIFY( names.contains( "Amsterdam" ) ); if ( !names.contains( "New York" ) ) { @@ -304,6 +310,39 @@ void LocaleTests::testComplexZones() { using namespace CalamaresUtils::Locale; + ZonesModel zones; + RegionalZonesModel europe( &zones ); + + QStringList names; + displayedNames( zones, names ); + QVERIFY( names.contains( "New York" ) ); + QVERIFY( names.contains( "Prague" ) ); + QVERIFY( names.contains( "Abidjan" ) ); + + // No region set + displayedNames( europe, names ); + QVERIFY( names.contains( "New York" ) ); + QVERIFY( names.contains( "Prague" ) ); + QVERIFY( names.contains( "Abidjan" ) ); + + // Now filter + europe.setRegion( "Europe" ); + displayedNames( europe, names ); + QVERIFY( !names.contains( "New York" ) ); + QVERIFY( names.contains( "Prague" ) ); + QVERIFY( !names.contains( "Abidjan" ) ); + + europe.setRegion( "America" ); + displayedNames( europe, names ); + QVERIFY( names.contains( "New York" ) ); + QVERIFY( !names.contains( "Prague" ) ); + QVERIFY( !names.contains( "Abidjan" ) ); + + europe.setRegion( "Africa" ); + displayedNames( europe, names ); + QVERIFY( !names.contains( "New York" ) ); + QVERIFY( !names.contains( "Prague" ) ); + QVERIFY( names.contains( "Abidjan" ) ); } QTEST_GUILESS_MAIN( LocaleTests ) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 05c082738..3d517940a 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -170,7 +170,6 @@ public: double longitude ); QString tr() const override; -private: QString m_region; QString m_country; double m_latitude; @@ -318,7 +317,7 @@ RegionsModel::RegionsModel( QObject* parent ) RegionsModel::~RegionsModel() {} int -RegionsModel::rowCount( const QModelIndex& parent ) const +RegionsModel::rowCount( const QModelIndex& ) const { return m_private->m_regions.count(); } @@ -359,7 +358,7 @@ ZonesModel::ZonesModel( QObject* parent ) ZonesModel::~ZonesModel() {} int -ZonesModel::rowCount( const QModelIndex& parent ) const +ZonesModel::rowCount( const QModelIndex& ) const { return m_private->m_zones.count(); } @@ -390,6 +389,43 @@ ZonesModel::roleNames() const return { { NameRole, "name" }, { KeyRole, "key" } }; } +RegionalZonesModel::RegionalZonesModel( CalamaresUtils::Locale::ZonesModel* source, QObject* parent ) + : QSortFilterProxyModel( parent ) + , m_private( privateInstance() ) +{ + setSourceModel( source ); +} + +RegionalZonesModel::~RegionalZonesModel() {} + +void +RegionalZonesModel::setRegion( const QString& r ) +{ + if ( r != m_region ) + { + m_region = r; + invalidateFilter(); + emit regionChanged( r ); + } +} + +bool +RegionalZonesModel::filterAcceptsRow( int sourceRow, const QModelIndex& ) const +{ + if ( m_region.isEmpty() ) + { + return true; + } + + if ( sourceRow < 0 || sourceRow >= m_private->m_zones.count() ) + { + return false; + } + + const auto& zone = m_private->m_zones[ sourceRow ]; + return ( zone.m_region == m_region ); +} + } // namespace Locale } // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index d87a5a57a..cb91a8361 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -26,6 +26,7 @@ #include #include +#include #include namespace CalamaresUtils @@ -85,6 +86,30 @@ private: Private* m_private; }; +class DLLEXPORT RegionalZonesModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY( QString region READ region WRITE setRegion NOTIFY regionChanged ) + +public: + RegionalZonesModel( ZonesModel* source, QObject* parent = nullptr ); + ~RegionalZonesModel() override; + + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + + QString region() const { return m_region; } + +public Q_SLOTS: + void setRegion( const QString& r ); + +signals: + void regionChanged( const QString& ); + +private: + Private* m_private; + QString m_region; +}; + } // namespace Locale } // namespace CalamaresUtils From 10fb5b95c7ca681c9d4f633255b70ffc31ed8657 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 17:52:41 +0200 Subject: [PATCH 30/91] [libcalamares] Split out CStringPair into TranslatableString The (renamed) class TranslatableString keeps a key value (e.g. New_York) and a human-readable version around; the human-readable one is passed through QObject::tr() for translation on-the-fly. --- src/libcalamares/CMakeLists.txt | 1 + src/libcalamares/locale/TimeZone.cpp | 114 ++---------------- .../locale/TranslatableString.cpp | 89 ++++++++++++++ src/libcalamares/locale/TranslatableString.h | 68 +++++++++++ 4 files changed, 168 insertions(+), 104 deletions(-) create mode 100644 src/libcalamares/locale/TranslatableString.cpp create mode 100644 src/libcalamares/locale/TranslatableString.h diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 8e209f8a3..d38aa1124 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -56,6 +56,7 @@ set( libSources locale/Lookup.cpp locale/TimeZone.cpp locale/TranslatableConfiguration.cpp + locale/TranslatableString.cpp # Modules modulesystem/InstanceKey.cpp diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 3d517940a..b19ad7e1e 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -22,6 +22,7 @@ #include "TimeZone.h" +#include "locale/TranslatableString.h" #include "utils/Logger.h" #include "utils/String.h" @@ -30,6 +31,11 @@ static const char TZ_DATA_FILE[] = "/usr/share/zoneinfo/zone.tab"; +namespace CalamaresUtils +{ +namespace Locale +{ + /** @brief Turns a string longitude or latitude notation into a double * * This handles strings like "+4230+00131" from zone.tab, @@ -63,104 +69,8 @@ getRightGeoLocation( QString str ) return sign * num; } -/** @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 the human-readable part - * may need to be translated through tr(), and that takes a char*. - * 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 ); - explicit CStringPair( const QString& s ); - 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; } - bool operator<( const CStringPair& other ) const { return m_key < other.m_key; } - -protected: - char* m_human = nullptr; - QString m_key; -}; - -/** @brief Massage an identifier into a human-readable form - * - * Makes a copy of @p s, caller must free() it. - */ -static char* -munge( const char* s ) -{ - char* t = strdup( s ); - if ( !t ) - { - return nullptr; - } - - // replace("_"," ") in the Python script - char* p = t; - while ( *p ) - { - if ( ( *p ) == '_' ) - { - *p = ' '; - } - ++p; - } - - return t; -} - -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 ) -{ -} - -CStringPair::CStringPair( const char* s1 ) - : m_human( s1 ? munge( s1 ) : nullptr ) - , m_key( s1 ? QString( s1 ) : QString() ) -{ -} - -CStringPair::CStringPair( const QString& s ) - : m_human( munge( s.toUtf8().constData() ) ) - , m_key( s ) -{ -} - - -CStringPair::~CStringPair() -{ - free( m_human ); -} - - -class TimeZoneData : public CStringPair +class TimeZoneData : public TranslatableString { public: TimeZoneData( const QString& region, @@ -181,7 +91,7 @@ TimeZoneData::TimeZoneData( const QString& region, const QString& country, double latitude, double longitude ) - : CStringPair( zone ) + : TranslatableString( zone ) , m_region( region ) , m_country( country ) , m_latitude( latitude ) @@ -197,10 +107,10 @@ TimeZoneData::tr() const } -class RegionData : public CStringPair +class RegionData : public TranslatableString { public: - using CStringPair::CStringPair; + using TranslatableString::TranslatableString; QString tr() const override; }; @@ -282,10 +192,6 @@ loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData >& zones ) } } -namespace CalamaresUtils -{ -namespace Locale -{ struct Private { diff --git a/src/libcalamares/locale/TranslatableString.cpp b/src/libcalamares/locale/TranslatableString.cpp new file mode 100644 index 000000000..9200c8d65 --- /dev/null +++ b/src/libcalamares/locale/TranslatableString.cpp @@ -0,0 +1,89 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + */ +#include "TranslatableString.h" + + +/** @brief Massage an identifier into a human-readable form + * + * Makes a copy of @p s, caller must free() it. + */ +static char* +munge( const char* s ) +{ + char* t = strdup( s ); + if ( !t ) + { + return nullptr; + } + + // replace("_"," ") in the Python script + char* p = t; + while ( *p ) + { + if ( ( *p ) == '_' ) + { + *p = ' '; + } + ++p; + } + + return t; +} + +namespace CalamaresUtils +{ +namespace Locale +{ + +TranslatableString::TranslatableString( TranslatableString&& 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 ); +} + +TranslatableString::TranslatableString( const TranslatableString& t ) + : m_human( t.m_human ? strdup( t.m_human ) : nullptr ) + , m_key( t.m_key ) +{ +} + +TranslatableString::TranslatableString( const char* s1 ) + : m_human( s1 ? munge( s1 ) : nullptr ) + , m_key( s1 ? QString( s1 ) : QString() ) +{ +} + +TranslatableString::TranslatableString( const QString& s ) + : m_human( munge( s.toUtf8().constData() ) ) + , m_key( s ) +{ +} + + +TranslatableString::~TranslatableString() +{ + free( m_human ); +} + +} // namespace Locale +} // namespace CalamaresUtils diff --git a/src/libcalamares/locale/TranslatableString.h b/src/libcalamares/locale/TranslatableString.h new file mode 100644 index 000000000..8347488f1 --- /dev/null +++ b/src/libcalamares/locale/TranslatableString.h @@ -0,0 +1,68 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + */ +#ifndef LOCALE_TRANSLATABLESTRING_H +#define LOCALE_TRANSLATABLESTRING_H + +#include + +namespace CalamaresUtils +{ +namespace Locale +{ + +/** @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 the human-readable part + * may need to be translated through tr(), and that takes a char* + * C-style strings. + */ +class TranslatableString +{ +public: + /// @brief An empty pair + TranslatableString() {} + /// @brief Given an identifier, create the pair + explicit TranslatableString( const char* s1 ); + explicit TranslatableString( const QString& s ); + TranslatableString( TranslatableString&& t ); + TranslatableString( const TranslatableString& ); + virtual ~TranslatableString(); + + /// @brief Give the localized human-readable form + virtual QString tr() const = 0; + QString key() const { return m_key; } + + bool operator==( const TranslatableString& other ) const { return m_key == other.m_key; } + bool operator<( const TranslatableString& other ) const { return m_key < other.m_key; } + +protected: + char* m_human = nullptr; + QString m_key; +}; + +} // namespace Locale +} // namespace CalamaresUtils + +#endif From 478a27576435f9f67499bd85cf5a15519973a2d8 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 18:01:24 +0200 Subject: [PATCH 31/91] [libcalamares] Make TimeZoneData public - Also make it a QObject so we can add properties and make it useful for QML consumption. --- src/libcalamares/locale/TimeZone.cpp | 34 ++++++---------------------- src/libcalamares/locale/TimeZone.h | 31 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index b19ad7e1e..73ac328d6 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -70,22 +70,6 @@ getRightGeoLocation( QString str ) } -class TimeZoneData : public TranslatableString -{ -public: - TimeZoneData( const QString& region, - const QString& zone, - const QString& country, - double latitude, - double longitude ); - QString tr() const override; - - QString m_region; - QString m_country; - double m_latitude; - double m_longitude; -}; - TimeZoneData::TimeZoneData( const QString& region, const QString& zone, const QString& country, @@ -122,7 +106,7 @@ RegionData::tr() const } static void -loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData >& zones ) +loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData* >& zones ) { QFile file( TZ_DATA_FILE ); if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) @@ -187,7 +171,7 @@ loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData >& zones ) { regions.append( std::move( r ) ); } - zones.append( TimeZoneData( region, zone, countryCode, latitude, longitude ) ); + zones.append( new TimeZoneData( region, zone, countryCode, latitude, longitude ) ); } } } @@ -196,7 +180,7 @@ loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData >& zones ) struct Private { QVector< RegionData > m_regions; - QVector< TimeZoneData > m_zones; + QVector< TimeZoneData* > m_zones; Private() { @@ -277,14 +261,14 @@ ZonesModel::data( const QModelIndex& index, int role ) const return QVariant(); } - const auto& zone = m_private->m_zones[ index.row() ]; + const auto* zone = m_private->m_zones[ index.row() ]; if ( role == NameRole ) { - return zone.tr(); + return zone->tr(); } if ( role == KeyRole ) { - return zone.key(); + return zone->key(); } return QVariant(); } @@ -329,13 +313,9 @@ RegionalZonesModel::filterAcceptsRow( int sourceRow, const QModelIndex& ) const } const auto& zone = m_private->m_zones[ sourceRow ]; - return ( zone.m_region == m_region ); + return ( zone->m_region == m_region ); } } // namespace Locale } // namespace CalamaresUtils - -#include "utils/moc-warnings.h" - -#include "TimeZone.moc" diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index cb91a8361..c1bcc96b9 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -24,6 +24,8 @@ #include "DllMacro.h" +#include "locale/TranslatableString.h" + #include #include #include @@ -34,6 +36,35 @@ namespace CalamaresUtils namespace Locale { struct Private; +class RegionalZonesModel; +class ZonesModel; + +class TimeZoneData : public QObject, TranslatableString +{ + friend class RegionalZonesModel; + friend class ZonesModel; + + Q_OBJECT + + Q_PROPERTY( QString region READ region CONSTANT ) + +public: + TimeZoneData( const QString& region, + const QString& zone, + const QString& country, + double latitude, + double longitude ); + QString tr() const override; + + QString region() const { return m_region; } + +private: + QString m_region; + QString m_country; + double m_latitude; + double m_longitude; +}; + /** @brief The list of timezone regions * From 245d4a8ef77207a102be41dc8cbe0e9d1ce4b957 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 22:30:04 +0200 Subject: [PATCH 32/91] [libcalamares] Add a find() to ZonesModel - Look up TZ data by region and zone name. --- src/libcalamares/locale/Tests.cpp | 16 ++++++++++++++++ src/libcalamares/locale/TimeZone.cpp | 13 +++++++++++++ src/libcalamares/locale/TimeZone.h | 7 +++++++ 3 files changed, 36 insertions(+) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 8a6a6ad84..db2ed2fe2 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -50,6 +50,7 @@ private Q_SLOTS: void testRegions(); void testSimpleZones(); void testComplexZones(); + void testTZLookup(); }; LocaleTests::LocaleTests() {} @@ -345,6 +346,21 @@ LocaleTests::testComplexZones() QVERIFY( names.contains( "Abidjan" ) ); } +void +LocaleTests::testTZLookup() +{ + using namespace CalamaresUtils::Locale; + ZonesModel zones; + + QVERIFY( zones.find( "America", "New_York" ) ); + QCOMPARE( zones.find( "America", "New_York" )->zone(), QStringLiteral( "New_York" ) ); + QCOMPARE( zones.find( "America", "New_York" )->tr(), QStringLiteral( "New York" ) ); + + QVERIFY( !zones.find( "Europe", "New_York" ) ); + QVERIFY( !zones.find( "America", "New York" ) ); +} + + QTEST_GUILESS_MAIN( LocaleTests ) #include "utils/moc-warnings.h" diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 73ac328d6..4e56b5af1 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -279,6 +279,19 @@ ZonesModel::roleNames() const return { { NameRole, "name" }, { KeyRole, "key" } }; } +const TimeZoneData* +ZonesModel::find( const QString& region, const QString& zone ) +{ + for ( const auto* p : m_private->m_zones ) + { + if ( p->region() == region && p->zone() == zone ) + { + return p; + } + } + return nullptr; +} + RegionalZonesModel::RegionalZonesModel( CalamaresUtils::Locale::ZonesModel* source, QObject* parent ) : QSortFilterProxyModel( parent ) , m_private( privateInstance() ) diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index c1bcc96b9..de23f07d2 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -57,6 +57,7 @@ public: QString tr() const override; QString region() const { return m_region; } + QString zone() const { return key(); } private: QString m_region; @@ -113,6 +114,12 @@ public: QHash< int, QByteArray > roleNames() const override; + /** @brief Look up TZ data based on its name. + * + * Returns @c nullptr if not found. + */ + const TimeZoneData* find( const QString& region, const QString& zone ); + private: Private* m_private; }; From 7ea2ad7dc6443611f34d291d449c8ae742e48255 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 23:26:17 +0200 Subject: [PATCH 33/91] [libcalamares] Add accessors for TZ data and region in the model It's convenient when e.g. QComboBox::currentData() gets the key "automatically", and the default role for that method is UserRole, so let the value of KeyRole overlap. --- src/libcalamares/locale/TimeZone.cpp | 12 +++++++----- src/libcalamares/locale/TimeZone.h | 12 ++++++++++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 4e56b5af1..d5d4938a8 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -262,15 +262,17 @@ ZonesModel::data( const QModelIndex& index, int role ) const } const auto* zone = m_private->m_zones[ index.row() ]; - if ( role == NameRole ) + switch ( role ) { + case NameRole: return zone->tr(); - } - if ( role == KeyRole ) - { + case KeyRole: return zone->key(); + case RegionRole: + return zone->region(); + default: + return QVariant(); } - return QVariant(); } QHash< int, QByteArray > diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index de23f07d2..afe32963c 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -54,11 +54,18 @@ public: const QString& country, double latitude, double longitude ); + TimeZoneData( const TimeZoneData& ) = delete; + TimeZoneData( TimeZoneData&& ) = delete; + QString tr() const override; 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; } + private: QString m_region; QString m_country; @@ -80,7 +87,7 @@ public: enum Roles { NameRole = Qt::DisplayRole, - KeyRole = Qt::UserRole + 1 + KeyRole = Qt::UserRole // So that currentData() will get the key }; RegionsModel( QObject* parent = nullptr ); @@ -103,7 +110,8 @@ public: enum Roles { NameRole = Qt::DisplayRole, - KeyRole = Qt::UserRole + 1 + KeyRole = Qt::UserRole, // So that currentData() will get the key + RegionRole = Qt::UserRole + 1 }; ZonesModel( QObject* parent = nullptr ); From 37c211fd14d8a6a35d02296e774be7da2e9c42be Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 5 Aug 2020 23:46:14 +0200 Subject: [PATCH 34/91] [libcalamares] Add an iterator for the full zones model --- src/libcalamares/locale/Tests.cpp | 26 +++++++++++++++++++ src/libcalamares/locale/TimeZone.cpp | 16 +++++++++++- src/libcalamares/locale/TimeZone.h | 39 +++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index db2ed2fe2..bb5365ee0 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -51,6 +51,7 @@ private Q_SLOTS: void testSimpleZones(); void testComplexZones(); void testTZLookup(); + void testTZIterator(); }; LocaleTests::LocaleTests() {} @@ -360,6 +361,31 @@ LocaleTests::testTZLookup() QVERIFY( !zones.find( "America", "New York" ) ); } +void +LocaleTests::testTZIterator() +{ + using namespace CalamaresUtils::Locale; + const ZonesModel zones; + + QVERIFY( zones.find( "Europe", "Rome" ) ); + + int count = 0; + bool seenRome = false; + bool seenGnome = false; + for ( auto it = zones.begin(); it; ++it ) + { + QVERIFY( *it ); + QVERIFY( !( *it )->zone().isEmpty() ); + seenRome |= ( *it )->zone() == QStringLiteral( "Rome" ); + seenGnome |= ( *it )->zone() == QStringLiteral( "Gnome" ); + count++; + } + + QVERIFY( seenRome ); + QVERIFY( !seenGnome ); + QCOMPARE( count, zones.rowCount( QModelIndex() ) ); +} + QTEST_GUILESS_MAIN( LocaleTests ) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index d5d4938a8..e8b861d69 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -282,7 +282,7 @@ ZonesModel::roleNames() const } const TimeZoneData* -ZonesModel::find( const QString& region, const QString& zone ) +ZonesModel::find( const QString& region, const QString& zone ) const { for ( const auto* p : m_private->m_zones ) { @@ -294,6 +294,20 @@ ZonesModel::find( const QString& region, const QString& zone ) return nullptr; } +ZonesModel::Iterator::operator bool() const +{ + return 0 <= m_index && m_index < m_p->m_zones.count(); +} + +const TimeZoneData* ZonesModel::Iterator::operator*() const +{ + if ( *this ) + { + return m_p->m_zones[ m_index ]; + } + return nullptr; +} + RegionalZonesModel::RegionalZonesModel( CalamaresUtils::Locale::ZonesModel* source, QObject* parent ) : QSortFilterProxyModel( parent ) , m_private( privateInstance() ) diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index afe32963c..9b8569b21 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -126,7 +126,44 @@ public: * * Returns @c nullptr if not found. */ - const TimeZoneData* find( const QString& region, const QString& zone ); + const TimeZoneData* find( const QString& region, const QString& zone ) const; + + /** @brief Iterator for testing purposes + * + * This is primarily for testing, but who knows, it might be useful + * elsewhere, and it's convenient when it can access Private. + * + * Iterates over all the zones in the model. Operator * may return + * a @c nullptr when the iterator is not valid. Typical usage: + * + * ``` + * for( auto it = model.begin(); it; ++it ) + * { + * const auto* zonedata = *it; + * ... + * } + */ + class Iterator + { + friend class ZonesModel; + Iterator( const Private* m ) + : m_index( 0 ) + , m_p( m ) + { + } + + public: + operator bool() const; + void operator++() { ++m_index; } + const TimeZoneData* operator*() const; + int index() const { return m_index; } + + private: + int m_index; + const Private* m_p; + }; + + Iterator begin() const { return Iterator( m_private ); } private: Private* m_private; From d814a3dba8479c2395d422694415611492d0cea9 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 00:56:13 +0200 Subject: [PATCH 35/91] [libcalamares] Sort the models before use - zones and regions alphabetically by key --- src/libcalamares/locale/Tests.cpp | 3 +++ src/libcalamares/locale/TimeZone.cpp | 38 ++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index bb5365ee0..dde199a7f 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -384,6 +384,9 @@ LocaleTests::testTZIterator() QVERIFY( seenRome ); QVERIFY( !seenGnome ); QCOMPARE( count, zones.rowCount( QModelIndex() ) ); + + QCOMPARE( zones.data( zones.index( 0 ), ZonesModel::RegionRole ).toString(), QStringLiteral( "Africa" ) ); + QCOMPARE( ( *zones.begin() )->zone(), QStringLiteral( "Abidjan" ) ); } diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index e8b861d69..974b73bfb 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -105,8 +105,11 @@ RegionData::tr() const return QObject::tr( m_human, "tz_regions" ); } +using RegionVector = QVector< RegionData* >; +using ZoneVector = QVector< TimeZoneData* >; + static void -loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData* >& zones ) +loadTZData( RegionVector& regions, ZoneVector& zones ) { QFile file( TZ_DATA_FILE ); if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) @@ -166,10 +169,18 @@ loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData* >& zones ) } // Now we have region, zone, country, lat and longitude - RegionData r( region ); - if ( regions.indexOf( r ) < 0 ) + const RegionData* existingRegion = nullptr; + for ( const auto* p : regions ) { - regions.append( std::move( r ) ); + if ( p->key() == region ) + { + existingRegion = p; + break; + } + } + if ( !existingRegion ) + { + regions.append( new RegionData( region ) ); } zones.append( new TimeZoneData( region, zone, countryCode, latitude, longitude ) ); } @@ -179,8 +190,8 @@ loadTZData( QVector< RegionData >& regions, QVector< TimeZoneData* >& zones ) struct Private { - QVector< RegionData > m_regions; - QVector< TimeZoneData* > m_zones; + RegionVector m_regions; + ZoneVector m_zones; Private() { @@ -188,6 +199,17 @@ struct Private m_zones.reserve( 452 ); // wc -l /usr/share/zoneinfo/zone.tab loadTZData( m_regions, m_zones ); + + std::sort( m_regions.begin(), m_regions.end(), []( const RegionData* lhs, const RegionData* rhs ) { + return lhs->key() < rhs->key(); + } ); + std::sort( m_zones.begin(), m_zones.end(), []( const TimeZoneData* lhs, const TimeZoneData* rhs ) { + if ( lhs->region() == rhs->region() ) + { + return lhs->zone() < rhs->zone(); + } + return lhs->region() < rhs->region(); + } ); } }; @@ -223,11 +245,11 @@ RegionsModel::data( const QModelIndex& index, int role ) const const auto& region = m_private->m_regions[ index.row() ]; if ( role == NameRole ) { - return region.tr(); + return region->tr(); } if ( role == KeyRole ) { - return region.key(); + return region->key(); } return QVariant(); } From 626dd038da7bb305d513a2957c7178356f04fb50 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 01:27:03 +0200 Subject: [PATCH 36/91] [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 --- src/modules/locale/Config.cpp | 28 ++-- src/modules/locale/Config.h | 52 ++++--- src/modules/locale/LocalePage.cpp | 28 ++-- src/modules/locale/LocalePage.h | 2 +- src/modules/locale/Tests.cpp | 130 +++++++----------- .../locale/timezonewidget/timezonewidget.cpp | 34 ++--- .../locale/timezonewidget/timezonewidget.h | 12 +- 7 files changed, 108 insertions(+), 178 deletions(-) diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index 7a49525f2..96d279477 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -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 ) { diff --git a/src/modules/locale/Config.h b/src/modules/locale/Config.h index e9a8e6373..fccd3822e 100644 --- a/src/modules/locale/Config.h +++ b/src/modules/locale/Config.h @@ -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 * diff --git a/src/modules/locale/LocalePage.cpp b/src/modules/locale/LocalePage.cpp index c10b2dee9..d4ad6854e 100644 --- a/src/modules/locale/LocalePage.cpp +++ b/src/modules/locale/LocalePage.cpp @@ -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 ) { diff --git a/src/modules/locale/LocalePage.h b/src/modules/locale/LocalePage.h index bf41f8f69..4f2d321b5 100644 --- a/src/modules/locale/LocalePage.h +++ b/src/modules/locale/LocalePage.h @@ -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(); diff --git a/src/modules/locale/Tests.cpp b/src/modules/locale/Tests.cpp index af37a664b..e7fbb10f2 100644 --- a/src/modules/locale/Tests.cpp +++ b/src/modules/locale/Tests.cpp @@ -22,6 +22,7 @@ #include "timezonewidget/TimeZoneImage.h" #include "locale/TimeZone.h" +#include "utils/Logger.h" #include @@ -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 ); diff --git a/src/modules/locale/timezonewidget/timezonewidget.cpp b/src/modules/locale/timezonewidget/timezonewidget.cpp index 0972e3296..b1d3cfeaa 100644 --- a/src/modules/locale/timezonewidget/timezonewidget.cpp +++ b/src/modules/locale/timezonewidget/timezonewidget.cpp @@ -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(); } } } diff --git a/src/modules/locale/timezonewidget/timezonewidget.h b/src/modules/locale/timezonewidget/timezonewidget.h index 6bb94c0dd..c15570b52 100644 --- a/src/modules/locale/timezonewidget/timezonewidget.h +++ b/src/modules/locale/timezonewidget/timezonewidget.h @@ -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 ); From ab69e7c83a35e729a356139ca34210847765c26d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 01:52:50 +0200 Subject: [PATCH 37/91] [libcalamares] Add API for geographical lookup - find a zone given lat, lon -- with a failing test and a bogus implementation. --- src/libcalamares/locale/Tests.cpp | 10 ++++++++++ src/libcalamares/locale/TimeZone.cpp | 6 ++++++ src/libcalamares/locale/TimeZone.h | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index dde199a7f..081d0ced5 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -52,6 +52,7 @@ private Q_SLOTS: void testComplexZones(); void testTZLookup(); void testTZIterator(); + void testLocationLookup(); }; LocaleTests::LocaleTests() {} @@ -389,6 +390,15 @@ LocaleTests::testTZIterator() QCOMPARE( ( *zones.begin() )->zone(), QStringLiteral( "Abidjan" ) ); } +void +LocaleTests::testLocationLookup() +{ + const CalamaresUtils::Locale::ZonesModel zones; + + QVERIFY( zones.find( 50.0, 0.0 ) ); + QCOMPARE( zones.find( 50.0, 0.0 )->zone(), QStringLiteral( "London" ) ); +} + QTEST_GUILESS_MAIN( LocaleTests ) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 974b73bfb..eaf7698ea 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -316,6 +316,12 @@ ZonesModel::find( const QString& region, const QString& zone ) const return nullptr; } +const TimeZoneData* +ZonesModel::find( double latitude, double longitude ) const +{ + return nullptr; +} + ZonesModel::Iterator::operator bool() const { return 0 <= m_index && m_index < m_p->m_zones.count(); diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 9b8569b21..55fdda60a 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -128,6 +128,12 @@ public: */ const TimeZoneData* find( const QString& region, const QString& zone ) const; + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone to the given lat and lon. + */ + const TimeZoneData* find( double latitude, double longitude ) const; + /** @brief Iterator for testing purposes * * This is primarily for testing, but who knows, it might be useful From 9e274aac07b4781a178b6428dbef72f82c8fe75a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 10:47:47 +0200 Subject: [PATCH 38/91] [libcalamares] Make ZonesModel more QML-friendly - expose TZ lookup (as a QObject*, which QML needs) - C++ code should use find(), which is safer --- src/libcalamares/locale/TimeZone.cpp | 36 ++++++++++++++++++++++--- src/libcalamares/locale/TimeZone.h | 39 ++++++++++++++++------------ 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index eaf7698ea..7143d7d33 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -35,6 +35,9 @@ namespace CalamaresUtils { namespace Locale { +class RegionData; +using RegionVector = QVector< RegionData* >; +using ZoneVector = QVector< TimeZoneData* >; /** @brief Turns a string longitude or latitude notation into a double * @@ -81,6 +84,7 @@ TimeZoneData::TimeZoneData( const QString& region, , m_latitude( latitude ) , m_longitude( longitude ) { + setObjectName( region + '/' + zone ); } QString @@ -105,9 +109,6 @@ RegionData::tr() const return QObject::tr( m_human, "tz_regions" ); } -using RegionVector = QVector< RegionData* >; -using ZoneVector = QVector< TimeZoneData* >; - static void loadTZData( RegionVector& regions, ZoneVector& zones ) { @@ -188,8 +189,10 @@ loadTZData( RegionVector& regions, ZoneVector& zones ) } -struct Private +class Private : public QObject { + Q_OBJECT +public: RegionVector m_regions; ZoneVector m_zones; @@ -210,6 +213,11 @@ struct Private } return lhs->region() < rhs->region(); } ); + + for ( auto* z : m_zones ) + { + z->setParent( this ); + } } }; @@ -322,6 +330,22 @@ ZonesModel::find( double latitude, double longitude ) const return nullptr; } +QObject* +ZonesModel::lookup( double latitude, double longitude ) const +{ + const auto* p = find( latitude, longitude ); + if ( !p ) + { + p = find( "America", "New_York" ); + } + if ( !p ) + { + cWarning() << "No zone (not even New York) found, expect crashes."; + } + return const_cast< QObject* >( reinterpret_cast< const QObject* >( p ) ); +} + + ZonesModel::Iterator::operator bool() const { return 0 <= m_index && m_index < m_p->m_zones.count(); @@ -376,3 +400,7 @@ RegionalZonesModel::filterAcceptsRow( int sourceRow, const QModelIndex& ) const } // namespace Locale } // namespace CalamaresUtils + +#include "utils/moc-warnings.h" + +#include "TimeZone.moc" diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 55fdda60a..1a4ee03bf 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -35,7 +35,7 @@ namespace CalamaresUtils { namespace Locale { -struct Private; +class Private; class RegionalZonesModel; class ZonesModel; @@ -122,22 +122,7 @@ public: QHash< int, QByteArray > roleNames() const override; - /** @brief Look up TZ data based on its name. - * - * Returns @c nullptr if not found. - */ - const TimeZoneData* find( const QString& region, const QString& zone ) const; - - /** @brief Look up TZ data based on the location. - * - * Returns the nearest zone to the given lat and lon. - */ - const TimeZoneData* find( double latitude, double longitude ) const; - - /** @brief Iterator for testing purposes - * - * This is primarily for testing, but who knows, it might be useful - * elsewhere, and it's convenient when it can access Private. + /** @brief Iterator for the underlying list of zones * * Iterates over all the zones in the model. Operator * may return * a @c nullptr when the iterator is not valid. Typical usage: @@ -171,6 +156,26 @@ public: Iterator begin() const { return Iterator( m_private ); } +public Q_SLOTS: + /** @brief Look up TZ data based on its name. + * + * Returns @c nullptr if not found. + */ + const TimeZoneData* find( const QString& region, const QString& zone ) const; + + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone to the given lat and lon. + */ + const TimeZoneData* find( double latitude, double longitude ) const; + + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone, or New York. This is non-const for QML + * purposes, but the object should be considered const anyway. + */ + QObject* lookup( double latitude, double longitude ) const; + private: Private* m_private; }; From 296337d45dc44a61decf92f00e602af846cb2490 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 11:57:05 +0200 Subject: [PATCH 39/91] [libcalamares] Implement nearest-TZ lookup - This version, based on lat+lon lookup, handles wrap-around the globe at -180 W (which is very close to +180 E) - Test wrap-around-the-globe lookups --- src/libcalamares/locale/Tests.cpp | 15 +++++++++-- src/libcalamares/locale/TimeZone.cpp | 37 +++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 081d0ced5..e498ac039 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -395,8 +395,19 @@ LocaleTests::testLocationLookup() { const CalamaresUtils::Locale::ZonesModel zones; - QVERIFY( zones.find( 50.0, 0.0 ) ); - QCOMPARE( zones.find( 50.0, 0.0 )->zone(), QStringLiteral( "London" ) ); + const auto* zone = zones.find( 50.0, 0.0 ); + QVERIFY( zone ); + QCOMPARE( zone->zone(), QStringLiteral( "London" ) ); + + + // Tarawa is close to "the other side of the world" from London + zone = zones.find( 0.0, 179.0 ); + QVERIFY( zone ); + QCOMPARE( zone->zone(), QStringLiteral( "Tarawa" ) ); + + zone = zones.find( 0.0, -179.0 ); + QVERIFY( zone ); + QCOMPARE( zone->zone(), QStringLiteral( "Tarawa" ) ); } diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 7143d7d33..364564232 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -327,7 +327,42 @@ ZonesModel::find( const QString& region, const QString& zone ) const const TimeZoneData* ZonesModel::find( double latitude, double longitude ) const { - return nullptr; + /* This is a somewhat derpy way of finding "closest", + * in that it considers one degree of separation + * either N/S or E/W equal to any other; this obviously + * falls apart at the poles. + */ + + double largestDifference = 720.0; + const TimeZoneData* closest = nullptr; + + for ( const auto* zone : m_private->m_zones ) + { + // Latitude doesn't wrap around: there is nothing north of 90 + double latitudeDifference = abs( zone->latitude() - latitude ); + + // Longitude **does** wrap around, so consider the case of -178 and 178 + // which differ by 4 degrees. + double westerly = qMin( zone->longitude(), longitude ); + double easterly = qMax( zone->longitude(), longitude ); + double longitudeDifference = 0.0; + if ( westerly < 0.0 && !( easterly < 0.0 ) ) + { + // Only if they're different signs can we have wrap-around. + longitudeDifference = qMin( abs( westerly - easterly ), abs( 360.0 + westerly - easterly ) ); + } + else + { + longitudeDifference = abs( westerly - easterly ); + } + + if ( latitudeDifference + longitudeDifference < largestDifference ) + { + largestDifference = latitudeDifference + longitudeDifference; + closest = zone; + } + } + return closest; } QObject* From ad3c0de936854cfda3396af0f400dff2a372dc1e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 12:43:49 +0200 Subject: [PATCH 40/91] [libcalamares] Reduce logging in POD manipulation --- src/libcalamares/geoip/Interface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libcalamares/geoip/Interface.cpp b/src/libcalamares/geoip/Interface.cpp index f8649f37a..47c826e1a 100644 --- a/src/libcalamares/geoip/Interface.cpp +++ b/src/libcalamares/geoip/Interface.cpp @@ -47,7 +47,6 @@ splitTZString( const QString& tz ) QStringList tzParts = timezoneString.split( '/', SplitSkipEmptyParts ); if ( tzParts.size() >= 2 ) { - cDebug() << "GeoIP reporting" << timezoneString; QString region = tzParts.takeFirst(); QString zone = tzParts.join( '/' ); return RegionZonePair( region, zone ); From 21f97db8fd95265ad9eada8f807673428b3350ce Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 14:19:27 +0200 Subject: [PATCH 41/91] [libcalamares] Offer translation lookup of regions --- src/libcalamares/locale/TimeZone.cpp | 12 ++++++++++++ src/libcalamares/locale/TimeZone.h | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 364564232..e2779281f 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -268,6 +268,18 @@ RegionsModel::roleNames() const return { { NameRole, "name" }, { KeyRole, "key" } }; } +QString +RegionsModel::tr( const QString& region ) const +{ + for ( const auto* p : m_private->m_regions ) + { + if ( p->key() == region ) + { + return p->tr(); + } + } + return region; +} ZonesModel::ZonesModel( QObject* parent ) : QAbstractListModel( parent ) diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 1a4ee03bf..1d4b4a8ff 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -47,6 +47,9 @@ class TimeZoneData : public QObject, TranslatableString Q_OBJECT Q_PROPERTY( QString region READ region CONSTANT ) + Q_PROPERTY( QString zone READ zone CONSTANT ) + Q_PROPERTY( QString name READ tr CONSTANT ) + Q_PROPERTY( QString countryCode READ country CONSTANT ) public: TimeZoneData( const QString& region, @@ -98,6 +101,14 @@ public: QHash< int, QByteArray > roleNames() const override; +public Q_SLOTS: + /** @brief Provides a human-readable version of the region + * + * Returns @p region unchanged if there is no such region + * or no translation for the region's name. + */ + QString tr( const QString& region ) const; + private: Private* m_private; }; From 04e53be934d13c08a63e4a5a12d8e0100ad15188 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 12:14:15 +0200 Subject: [PATCH 42/91] [locale] Repair test: don't re-init te occupied-pixels set each loop - while here, merge Tests.h to the cpp file - Fix build when debugging timezones (missed during earlier refactor) --- src/modules/locale/Config.cpp | 2 +- src/modules/locale/Tests.cpp | 30 ++++++++++++++++++++--- src/modules/locale/Tests.h | 45 ----------------------------------- 3 files changed, 28 insertions(+), 49 deletions(-) delete mode 100644 src/modules/locale/Tests.h diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index 96d279477..b8a6df437 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -368,7 +368,7 @@ getAdjustLiveTimezone( const QVariantMap& configurationMap, bool& adjustLiveTime adjustLiveTimezone = CalamaresUtils::getBool( configurationMap, "adjustLiveTimezone", Calamares::Settings::instance()->doChroot() ); #ifdef DEBUG_TIMEZONES - if ( m_adjustLiveTimezone ) + if ( adjustLiveTimezone ) { cWarning() << "Turning off live-timezone adjustments because debugging is on."; adjustLiveTimezone = false; diff --git a/src/modules/locale/Tests.cpp b/src/modules/locale/Tests.cpp index e7fbb10f2..de0be57c4 100644 --- a/src/modules/locale/Tests.cpp +++ b/src/modules/locale/Tests.cpp @@ -17,7 +17,6 @@ */ -#include "Tests.h" #include "LocaleConfiguration.h" #include "timezonewidget/TimeZoneImage.h" @@ -28,6 +27,26 @@ #include +class LocaleTests : public QObject +{ + Q_OBJECT +public: + LocaleTests(); + ~LocaleTests() override; + +private Q_SLOTS: + void initTestCase(); + // Check the sample config file is processed correctly + void testEmptyLocaleConfiguration(); + void testDefaultLocaleConfiguration(); + void testSplitLocaleConfiguration(); + + // Check the TZ images for consistency + void testTZImages(); // No overlaps in images + void testTZLocations(); // No overlaps in locations + void testSpecificLocations(); +}; + QTEST_MAIN( LocaleTests ) @@ -191,11 +210,12 @@ LocaleTests::testTZLocations() using namespace CalamaresUtils::Locale; ZonesModel zones; + QVERIFY( zones.rowCount( QModelIndex() ) > 100 ); + int overlapcount = 0; + std::set< QPoint > occupied; for ( auto it = zones.begin(); it; ++it ) { - std::set< QPoint > occupied; - const auto* zone = *it; QVERIFY( zone ); @@ -230,3 +250,7 @@ LocaleTests::testSpecificLocations() QEXPECT_FAIL( "", "Gibraltar and Ceuta are really close", Continue ); QVERIFY( gpos.y() < cpos.y() ); // Gibraltar is north of Ceuta } + +#include "utils/moc-warnings.h" + +#include "Tests.moc" diff --git a/src/modules/locale/Tests.h b/src/modules/locale/Tests.h deleted file mode 100644 index e01b1a25c..000000000 --- a/src/modules/locale/Tests.h +++ /dev/null @@ -1,45 +0,0 @@ -/* === This file is part of Calamares - === - * - * Copyright 2019-2020, Adriaan de Groot - * - * Calamares is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Calamares is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Calamares. If not, see . - */ - - -#ifndef TESTS_H -#define TESTS_H - -#include - -class LocaleTests : public QObject -{ - Q_OBJECT -public: - LocaleTests(); - ~LocaleTests() override; - -private Q_SLOTS: - void initTestCase(); - // Check the sample config file is processed correctly - void testEmptyLocaleConfiguration(); - void testDefaultLocaleConfiguration(); - void testSplitLocaleConfiguration(); - - // Check the TZ images for consistency - void testTZImages(); // No overlaps in images - void testTZLocations(); // No overlaps in locations - void testSpecificLocations(); -}; - -#endif From b36ad4c7f4c7e96718e67303690b27777d56da35 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 12:32:48 +0200 Subject: [PATCH 43/91] [locale] Add test for Config initialization - needs some massaging because Config otherwise depends on ModuleManager which is a UI class (for the Reasons), but we already have a BUILD_AS_TEST define for that purpose. - demonstrate a nullptr deref. --- src/modules/locale/CMakeLists.txt | 2 ++ src/modules/locale/Config.cpp | 2 ++ src/modules/locale/Tests.cpp | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/modules/locale/CMakeLists.txt b/src/modules/locale/CMakeLists.txt index a09bde282..6f965f041 100644 --- a/src/modules/locale/CMakeLists.txt +++ b/src/modules/locale/CMakeLists.txt @@ -36,7 +36,9 @@ calamares_add_test( localetest SOURCES Tests.cpp + Config.cpp LocaleConfiguration.cpp + SetTimezoneJob.cpp timezonewidget/TimeZoneImage.cpp DEFINITIONS SOURCE_DIR="${CMAKE_CURRENT_LIST_DIR}/images" diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index b8a6df437..ecd704186 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -436,11 +436,13 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) getStartingTimezone( configurationMap, m_startingTimezone ); getGeoIP( configurationMap, m_geoip ); +#ifndef BUILD_AS_TEST if ( m_geoip && m_geoip->isValid() ) { connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::modulesLoaded, this, &Config::startGeoIP ); } +#endif } Calamares::JobList diff --git a/src/modules/locale/Tests.cpp b/src/modules/locale/Tests.cpp index de0be57c4..52d4882a2 100644 --- a/src/modules/locale/Tests.cpp +++ b/src/modules/locale/Tests.cpp @@ -16,7 +16,7 @@ * along with Calamares. If not, see . */ - +#include "Config.h" #include "LocaleConfiguration.h" #include "timezonewidget/TimeZoneImage.h" @@ -45,6 +45,9 @@ private Q_SLOTS: void testTZImages(); // No overlaps in images void testTZLocations(); // No overlaps in locations void testSpecificLocations(); + + // Check the Config loading + void testConfigInitialization(); }; QTEST_MAIN( LocaleTests ) @@ -251,6 +254,16 @@ LocaleTests::testSpecificLocations() QVERIFY( gpos.y() < cpos.y() ); // Gibraltar is north of Ceuta } +void +LocaleTests::testConfigInitialization() +{ + Config c; + + QVERIFY( !c.currentLocation() ); + QVERIFY( !c.currentLocationStatus().isEmpty() ); +} + + #include "utils/moc-warnings.h" #include "Tests.moc" From eda14ce548b2fd0a0d397c6956c64b50dd0d38f5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 12:38:55 +0200 Subject: [PATCH 44/91] [locale] Avoid nullptr deref - when no location has been set at all, there's no sensible TZ to report; just leave it blank. In *practice* you won't hit this code from the Calamares UI before a location has been set, because the Config object is instantiated and then immediately configured, but from tests or unusual UIs it could be. --- src/modules/locale/Config.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index ecd704186..a6d3e5222 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -318,7 +318,9 @@ Config::setLCLocaleExplicitly( const QString& locale ) QString Config::currentLocationStatus() const { - return tr( "Set timezone to %1/%2." ).arg( m_currentLocation->region(), m_currentLocation->zone() ); + return tr( "Set timezone to %1/%2." ) + .arg( m_currentLocation ? m_currentLocation->region() : QString(), + m_currentLocation ? m_currentLocation->zone() : QString() ); } static inline QString From 15a8d629864d3de473b02b59fd6dec30b14a88db Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 12:40:24 +0200 Subject: [PATCH 45/91] [locale] Add a 'current timezone' strings to Config - status is a longer phrase - name is a short human-readable name - code is the internal code Code that writes its own "Timezone set to" messages can use the name, rather than the status. --- src/modules/locale/Config.cpp | 25 +++++++++++++++++++++++++ src/modules/locale/Config.h | 9 +++++++++ 2 files changed, 34 insertions(+) diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index a6d3e5222..228f863e5 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -192,6 +192,9 @@ Config::Config( QObject* parent ) QProcess::execute( "timedatectl", // depends on systemd { "set-timezone", location->region() + '/' + location->zone() } ); } + + emit currentTimezoneCodeChanged( currentTimezoneCode() ); + emit currentTimezoneNameChanged( currentTimezoneName() ); } ); auto prettyStatusNotify = [&]() { emit prettyStatusChanged( prettyStatus() ); }; @@ -265,6 +268,7 @@ Config::setCurrentLocation( const CalamaresUtils::Locale::TimeZoneData* location emit currentLCStatusChanged( currentLCStatus() ); } emit currentLocationChanged( m_currentLocation ); + // Other signals come from the LocationChanged signal } } @@ -323,6 +327,27 @@ Config::currentLocationStatus() const m_currentLocation ? m_currentLocation->zone() : QString() ); } +QString +Config::currentTimezoneCode() const +{ + if ( m_currentLocation ) + { + return m_currentLocation->region() + '/' + m_currentLocation->zone(); + } + return QString(); +} + +QString +Config::currentTimezoneName() const +{ + if ( m_currentLocation ) + { + return m_regionModel->tr( m_currentLocation->region() ) + '/' + m_currentLocation->tr(); + } + return QString(); +} + + static inline QString localeLabel( const QString& s ) { diff --git a/src/modules/locale/Config.h b/src/modules/locale/Config.h index fccd3822e..5754cde8d 100644 --- a/src/modules/locale/Config.h +++ b/src/modules/locale/Config.h @@ -48,7 +48,12 @@ class Config : public QObject Q_PROPERTY( QString currentLocationStatus READ currentLocationStatus NOTIFY currentLanguageStatusChanged ) Q_PROPERTY( QString currentLanguageStatus READ currentLanguageStatus NOTIFY currentLanguageStatusChanged ) Q_PROPERTY( QString currentLCStatus READ currentLCStatus NOTIFY currentLCStatusChanged ) + // Name are shorter human-readable names + // .. main difference is that status is a full sentence, like "Timezone is America/New York" + // while name is just "America/New York" (and the code, below, is "America/New_York") + Q_PROPERTY( QString currentTimezoneName READ currentTimezoneName NOTIFY currentTimezoneNameChanged ) // Code are internal identifiers, like "en_US.UTF-8" + Q_PROPERTY( QString currentTimezoneCode READ currentTimezoneCode NOTIFY currentTimezoneCodeChanged ) Q_PROPERTY( QString currentLanguageCode READ currentLanguageCode WRITE setLanguageExplicitly NOTIFY currentLanguageCodeChanged ) Q_PROPERTY( QString currentLCCode READ currentLCCode WRITE setLCLocaleExplicitly NOTIFY currentLCCodeChanged ) @@ -119,6 +124,8 @@ public Q_SLOTS: QString currentLanguageCode() const { return localeConfiguration().language(); } QString currentLCCode() const { return localeConfiguration().lc_numeric; } + QString currentTimezoneName() const; // human-readable + QString currentTimezoneCode() const; signals: void currentLocationChanged( const CalamaresUtils::Locale::TimeZoneData* location ) const; @@ -128,6 +135,8 @@ signals: void prettyStatusChanged( const QString& ) const; void currentLanguageCodeChanged( const QString& ) const; void currentLCCodeChanged( const QString& ) const; + void currentTimezoneCodeChanged( const QString& ) const; + void currentTimezoneNameChanged( const QString& ) const; private: /// A list of supported locale identifiers (e.g. "en_US.UTF-8") From 91cc5a2b4225d8a3a4f35dc90bebbd213ee217c9 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 14:47:55 +0200 Subject: [PATCH 46/91] [locale] Update the map-QML implementation - Config has suitable strings for displaying TZ information. Use them and automatic bindings. Don't update the strings manually. - Suggest online or offline TZ lookups based on what the distro wants. Edit the QML to pick online lookups (needs access to the geonames service, though). - Drop the variables that point at config and geoip: the Config object has a currentLocation, which is filled in by both the configuration and any GeoIP lookup -- it doesn't have city or country information though. --- src/modules/localeq/Map.qml | 53 ++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/modules/localeq/Map.qml b/src/modules/localeq/Map.qml index 080d7388a..06b4f7a1f 100644 --- a/src/modules/localeq/Map.qml +++ b/src/modules/localeq/Map.qml @@ -29,17 +29,12 @@ import QtPositioning 5.14 Column { width: parent.width - //Needs to come from .conf/geoip - property var configCity: "New York" - property var configCountry: "USA" - property var configTimezone: "America/New York" - property var geoipCity: "" //"Amsterdam" - property var geoipCountry: "" //"Netherlands" - property var geoipTimezone: "" //"Europe/Amsterdam" - // vars that will stay once connected - property var cityName: (geoipCity != "") ? geoipCity : configCity - property var countryName: (geoipCountry != "") ? geoipCountry : configCountry - property var timeZone: (geoipTimezone != "") ? geoipTimezone : configTimezone + // These are used by the map query to initially center the + // map on the user's likely location. They are updated by + // getIp() which does a more accurate GeoIP lookup than + // the default one in Calamares + property var cityName: "" + property var countryName: "" function getIp() { var xhr = new XMLHttpRequest @@ -51,9 +46,10 @@ Column { var ct = responseJSON.city var cy = responseJSON.country - tzText.text = "Timezone: " + tz cityName = ct countryName = cy + + config.setCurrentLocation(tz) } } @@ -63,7 +59,15 @@ Column { xhr.send() } - function getTz() { + /* This is an **accurate** TZ lookup method: it queries an + * online service for the TZ at the given coordinates. It + * requires an internet connection, though, and the distribution + * will need to have an account with geonames to not hit the + * daily query limit. + * + * See below, in MouseArea, for calling the right method. + */ + function getTzOnline() { var xhr = new XMLHttpRequest var latC = map.center.latitude var lonC = map.center.longitude @@ -73,16 +77,29 @@ Column { var responseJSON = JSON.parse(xhr.responseText) var tz2 = responseJSON.timezoneId - tzText.text = "Timezone: " + tz2 config.setCurrentLocation(tz2) } } + console.log("Online lookup", latC, lonC) // Needs to move to localeq.conf, each distribution will need their own account xhr.open("GET", "http://api.geonames.org/timezoneJSON?lat=" + latC + "&lng=" + lonC + "&username=SOME_USERNAME") xhr.send() } + /* This is a quick TZ lookup method: it uses the existing + * Calamares "closest TZ" code, which has lots of caveats. + * + * See below, in MouseArea, for calling the right method. + */ + function getTzOffline() { + var latC = map.center.latitude + var lonC = map.center.longitude + var tz = config.zonesModel.lookup(latC, lonC) + console.log("Offline lookup", latC, lonC) + config.setCurrentLocation(tz.region, tz.zone) + } + Rectangle { width: parent.width height: parent.height / 1.28 @@ -156,9 +173,8 @@ Column { map.center.latitude = coordinate.latitude map.center.longitude = coordinate.longitude - getTz(); - - console.log(coordinate.latitude, coordinate.longitude) + // Pick a TZ lookup method here (quick:offline, accurate:online) + getTzOffline(); } } } @@ -218,8 +234,7 @@ Column { Text { id: tzText - text: tzText.text - //text: qsTr("Timezone: %1").arg(timeZone) + text: qsTr("Timezone: %1").arg(config.currentTimezoneName) color: Kirigami.Theme.textColor anchors.centerIn: parent } From 32c8338a9ce540b9306a6610b3ff9843f6adb8bd Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 15:58:11 +0200 Subject: [PATCH 47/91] [locale] QML doesn't like const --- src/modules/locale/Config.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/modules/locale/Config.h b/src/modules/locale/Config.h index 5754cde8d..2a44f4d24 100644 --- a/src/modules/locale/Config.h +++ b/src/modules/locale/Config.h @@ -42,7 +42,7 @@ class Config : public QObject Q_PROPERTY( QAbstractItemModel* regionalZonesModel READ regionalZonesModel CONSTANT FINAL ) Q_PROPERTY( - const CalamaresUtils::Locale::TimeZoneData* currentLocation READ currentLocation NOTIFY currentLocationChanged ) + CalamaresUtils::Locale::TimeZoneData* currentLocation READ currentLocation_c NOTIFY currentLocationChanged ) // Status are complete, human-readable, messages Q_PROPERTY( QString currentLocationStatus READ currentLocationStatus NOTIFY currentLanguageStatusChanged ) @@ -94,9 +94,16 @@ public: const CalamaresUtils::Locale::TimeZoneData* currentLocation() const { return m_currentLocation; } + /// Special case, set location from starting timezone if not already set void setCurrentLocation(); +private: + CalamaresUtils::Locale::TimeZoneData* currentLocation_c() const + { + return const_cast< CalamaresUtils::Locale::TimeZoneData* >( m_currentLocation ); + } + public Q_SLOTS: /// Set a language by user-choice, overriding future location changes void setLanguageExplicitly( const QString& language ); From 71ca1e154438b62deddeb5505509c9321d3eb288 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 15:49:55 +0200 Subject: [PATCH 48/91] [localeq] Pick up Config changes before showing the module --- src/modules/localeq/LocaleQmlViewStep.cpp | 9 ++++++++- src/modules/localeq/LocaleQmlViewStep.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/modules/localeq/LocaleQmlViewStep.cpp b/src/modules/localeq/LocaleQmlViewStep.cpp index ead2e2673..cbcc5f78e 100644 --- a/src/modules/localeq/LocaleQmlViewStep.cpp +++ b/src/modules/localeq/LocaleQmlViewStep.cpp @@ -79,9 +79,16 @@ LocaleQmlViewStep::jobs() const return m_config->createJobs(); } +void +LocaleQmlViewStep::onActivate() +{ + m_config->setCurrentLocation(); // Finalize the location + QmlViewStep::onActivate(); +} + void LocaleQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { m_config->setConfigurationMap( configurationMap ); - Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last + QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last } diff --git a/src/modules/localeq/LocaleQmlViewStep.h b/src/modules/localeq/LocaleQmlViewStep.h index 3d73c6f79..e2b8a9e13 100644 --- a/src/modules/localeq/LocaleQmlViewStep.h +++ b/src/modules/localeq/LocaleQmlViewStep.h @@ -43,6 +43,8 @@ public: bool isAtBeginning() const override; bool isAtEnd() const override; + virtual void onActivate() override; + Calamares::JobList jobs() const override; void setConfigurationMap( const QVariantMap& configurationMap ) override; From c69bd972e9f9b96fe1028f164e2a507d7c0b8f99 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 16:05:58 +0200 Subject: [PATCH 49/91] [localeq] Demonstrate "offline" lookups - we can do GeoIP and GeoNames lookups, **or** - use Calamares's internal GeoIP lookup and country / city hints. The online version is much more accurate, but costs more lookups; in these examples, set it all to "offline" and document what needs to change (code edit) to use the online version. It's probably a good beginner job to introduce a bool in localeq.qml to switch the behaviors. --- src/modules/localeq/Map.qml | 23 +++++++++++++++++++++-- src/modules/localeq/localeq.qml | 8 ++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/modules/localeq/Map.qml b/src/modules/localeq/Map.qml index 06b4f7a1f..d414825dd 100644 --- a/src/modules/localeq/Map.qml +++ b/src/modules/localeq/Map.qml @@ -36,7 +36,12 @@ Column { property var cityName: "" property var countryName: "" - function getIp() { + /* This is an extra GeoIP lookup, which will find better-accuracy + * location data for the user's IP, and then sets the current timezone + * and map location. Call it from Component.onCompleted so that + * it happens "on time" before the page is shown. + */ + function getIpOnline() { var xhr = new XMLHttpRequest xhr.onreadystatechange = function() { @@ -59,6 +64,16 @@ Column { xhr.send() } + /* This is an "offline" GeoIP lookup -- it just follows what + * Calamares itself has figured out with its GeoIP or configuration. + * Call it from the **Component** onActivate() -- in localeq.qml -- + * so it happens as the page is shown. + */ + function getIpOffline() { + cityName = config.currentLocation.zone + countryName = config.currentLocation.countryCode + } + /* This is an **accurate** TZ lookup method: it queries an * online service for the TZ at the given coordinates. It * requires an internet connection, though, and the distribution @@ -239,7 +254,11 @@ Column { anchors.centerIn: parent } - Component.onCompleted: getIp(); + /* If you want an extra (and accurate) GeoIP lookup, + * enable this one and disable the offline lookup in + * onActivate(). + Component.onCompleted: getIpOnline(); + */ } } diff --git a/src/modules/localeq/localeq.qml b/src/modules/localeq/localeq.qml index 49719db65..1a250f5c1 100644 --- a/src/modules/localeq/localeq.qml +++ b/src/modules/localeq/localeq.qml @@ -29,6 +29,14 @@ Page { width: 800 height: 550 + function onActivate() { + /* If you want the map to follow Calamares's GeoIP + * lookup or configuration, call the update function + * here, and disable the one at onCompleted in Map.qml. + */ + if (Network.hasInternet) { image.item.getIpOffline() } + } + Loader { id: image anchors.horizontalCenter: parent.horizontalCenter From 52d1c8f88a077170f7f7e11d0f5b61b82e68e0b6 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 18:32:51 +0200 Subject: [PATCH 50/91] [locale] Explicitly update GS from the locale step - refactor into some free functions (out of the lambda's for connecting) - introduce new method to call from onLeave(), matching previous widget behavior --- src/modules/locale/Config.cpp | 70 +++++++++++++++++------ src/modules/locale/Config.h | 1 + src/modules/locale/LocaleViewStep.cpp | 2 + src/modules/localeq/LocaleQmlViewStep.cpp | 6 ++ src/modules/localeq/LocaleQmlViewStep.h | 1 + 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index 228f863e5..5bbe20038 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -148,6 +148,45 @@ loadLocales( const QString& localeGenPath ) return localeGenLines; } +static bool +updateGSLocation( Calamares::GlobalStorage* gs, const CalamaresUtils::Locale::TimeZoneData* location ) +{ + const QString regionKey = QStringLiteral( "locationRegion" ); + const QString zoneKey = QStringLiteral( "locationZone" ); + + if ( !location ) + { + if ( gs->contains( regionKey ) || gs->contains( zoneKey ) ) + { + gs->remove( regionKey ); + gs->remove( zoneKey ); + return true; + } + return false; + } + + // Update the GS region and zone (and possibly the live timezone) + bool locationChanged + = ( location->region() != gs->value( regionKey ) ) || ( location->zone() != gs->value( zoneKey ) ); + + gs->insert( regionKey, location->region() ); + gs->insert( zoneKey, location->zone() ); + + return locationChanged; +} + +static void +updateGSLocale( Calamares::GlobalStorage* gs, const LocaleConfiguration& locale ) +{ + auto map = locale.toMap(); + QVariantMap vm; + for ( auto it = map.constBegin(); it != map.constEnd(); ++it ) + { + vm.insert( it.key(), it.value() ); + } + gs->insert( "localeConf", vm ); +} + Config::Config( QObject* parent ) : QObject( parent ) , m_regionModel( std::make_unique< CalamaresUtils::Locale::RegionsModel >() ) @@ -166,31 +205,17 @@ Config::Config( QObject* parent ) } ); 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 ); + updateGSLocale( Calamares::JobQueue::instance()->globalStorage(), localeConfiguration() ); } ); connect( this, &Config::currentLocationChanged, [&]() { - auto* gs = Calamares::JobQueue::instance()->globalStorage(); + const bool locationChanged + = updateGSLocation( Calamares::JobQueue::instance()->globalStorage(), currentLocation() ); - // 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() } ); + { "set-timezone", currentTimezoneCode() } ); } emit currentTimezoneCodeChanged( currentTimezoneCode() ); @@ -487,6 +512,15 @@ Config::createJobs() return list; } +void +Config::finalizeGlobalStorage() const +{ + auto* gs = Calamares::JobQueue::instance()->globalStorage(); + updateGSLocale( gs, localeConfiguration() ); + updateGSLocation( gs, currentLocation() ); +} + + void Config::startGeoIP() { diff --git a/src/modules/locale/Config.h b/src/modules/locale/Config.h index 2a44f4d24..d95606d99 100644 --- a/src/modules/locale/Config.h +++ b/src/modules/locale/Config.h @@ -66,6 +66,7 @@ public: ~Config(); void setConfigurationMap( const QVariantMap& ); + void finalizeGlobalStorage() const; Calamares::JobList createJobs(); /// locale configuration (LC_* and LANG) based solely on the current location. diff --git a/src/modules/locale/LocaleViewStep.cpp b/src/modules/locale/LocaleViewStep.cpp index a85c87e4f..9ffb96c25 100644 --- a/src/modules/locale/LocaleViewStep.cpp +++ b/src/modules/locale/LocaleViewStep.cpp @@ -138,6 +138,7 @@ LocaleViewStep::jobs() const void LocaleViewStep::onActivate() { + m_config->setCurrentLocation(); // Finalize the location if ( !m_actualWidget ) { setUpPage(); @@ -149,6 +150,7 @@ LocaleViewStep::onActivate() void LocaleViewStep::onLeave() { + m_config->finalizeGlobalStorage(); } diff --git a/src/modules/localeq/LocaleQmlViewStep.cpp b/src/modules/localeq/LocaleQmlViewStep.cpp index cbcc5f78e..d60664a8f 100644 --- a/src/modules/localeq/LocaleQmlViewStep.cpp +++ b/src/modules/localeq/LocaleQmlViewStep.cpp @@ -86,6 +86,12 @@ LocaleQmlViewStep::onActivate() QmlViewStep::onActivate(); } +void +LocaleQmlViewStep::onLeave() +{ + m_config->finalizeGlobalStorage(); +} + void LocaleQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { diff --git a/src/modules/localeq/LocaleQmlViewStep.h b/src/modules/localeq/LocaleQmlViewStep.h index e2b8a9e13..bbafd5abb 100644 --- a/src/modules/localeq/LocaleQmlViewStep.h +++ b/src/modules/localeq/LocaleQmlViewStep.h @@ -44,6 +44,7 @@ public: bool isAtEnd() const override; virtual void onActivate() override; + virtual void onLeave() override; Calamares::JobList jobs() const override; From c64aefe43c50bfafc2fd5f50fb60bfc7bb11d09a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 22:50:11 +0200 Subject: [PATCH 51/91] [libcalamares] Remove unused include, declaration --- src/libcalamares/GlobalStorage.cpp | 1 - src/libcalamares/GlobalStorage.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index 341fc3892..07af5e1b1 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -22,7 +22,6 @@ */ #include "GlobalStorage.h" -#include "JobQueue.h" #include "utils/Logger.h" #include "utils/Units.h" diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h index a2848f888..b242cc205 100644 --- a/src/libcalamares/GlobalStorage.h +++ b/src/libcalamares/GlobalStorage.h @@ -24,8 +24,6 @@ #ifndef CALAMARES_GLOBALSTORAGE_H #define CALAMARES_GLOBALSTORAGE_H -#include "CalamaresConfig.h" - #include #include #include @@ -33,8 +31,6 @@ namespace Calamares { -class DebugWindow; - class GlobalStorage : public QObject { Q_OBJECT From 527449a10215d78647089c6bb37520063b16c41f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 22:54:37 +0200 Subject: [PATCH 52/91] [libcalamares] Improve GS debugDump() formatting --- src/libcalamares/GlobalStorage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index 07af5e1b1..f9716e661 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -88,9 +88,10 @@ GlobalStorage::value( const QString& key ) const void GlobalStorage::debugDump() const { + cDebug() << "GlobalStorage" << Logger::Pointer(this) << m.count() << "items"; for ( auto it = m.cbegin(); it != m.cend(); ++it ) { - cDebug() << it.key() << '\t' << it.value(); + cDebug() << Logger::SubEntry << it.key() << '\t' << it.value(); } } From 104452513bbb469d576c3cc9b8b1d9750b3b9342 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 23:14:44 +0200 Subject: [PATCH 53/91] [libcalamares] Document GS - write apidox for all of GlobalStorage - while here, polish up the SPDX bits --- src/libcalamares/GlobalStorage.cpp | 4 +- src/libcalamares/GlobalStorage.h | 91 +++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 11 deletions(-) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index f9716e661..519b1d0d6 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -2,6 +2,7 @@ * * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +17,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "GlobalStorage.h" diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h index b242cc205..782d443d4 100644 --- a/src/libcalamares/GlobalStorage.h +++ b/src/libcalamares/GlobalStorage.h @@ -2,6 +2,7 @@ * * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +17,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #ifndef CALAMARES_GLOBALSTORAGE_H @@ -31,21 +29,65 @@ namespace Calamares { +/** @brief Storage for data that passes between Calamares modules. + * + * The Global Storage is global to the Calamars JobQueue and + * everything that depends on that: all of its modules use the + * same instance of the JobQueue, and so of the Global Storage. + * + * GS is used to pass data between modules; there is only convention + * about what keys are used, and individual modules should document + * what they put in to GS or what they expect to find in it. + * + * GS behaves as a basic key-value store, with a QVariantMap behind + * it. Any QVariant can be put into the storage, and the signal + * changed() is emitted when any data is modified. + * + * In general, see QVariantMap (possibly after calling data()) for details. + * + * This class is not thread-safe, but as long as JobQueue is, that's ok + * because only one module is active at a time. + */ class GlobalStorage : public QObject { Q_OBJECT public: + /** @brief Create a GS object + * + * **Generally** there is only one GS object (hence, "global") which + * is owned by the JobQueue instance (which is a singleton). However, + * it is possible to create more GS objects. + */ explicit GlobalStorage( QObject* parent = nullptr ); - //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. + /** @brief Insert a key and value into the store + * + * The @p value is added to the store with key @p key. If @p key + * already exists in the store, its existing value is overwritten. + * The changed() signal is emitted regardless. + */ void insert( const QString& key, const QVariant& value ); + /** @brief Removes a key and its value + * + * The @p key is removed from the store. If the @p key does not + * exist, nothing happens. changed() is emitted regardless. + * + * @return the number of keys remaining + */ int remove( const QString& key ); - /// @brief dump keys and values to the debug log + /** @brief dump keys and values to the debug log + * + * All the keys and their values are written to the debug log. + * See save() for caveats: this can leak sensitive information. + */ void debugDump() const; /** @brief write as JSON to the given filename + * + * The file named @p filename is overwritten with a JSON representation + * of the entire global storage (this may be structured, for instance + * if maps or lists have been inserted). * * No tidying, sanitization, or censoring is done -- for instance, * the user module sets a slightly-obscured password in global storage, @@ -58,7 +100,8 @@ public: * * No tidying, sanitization, or censoring is done. * The JSON file is read and each key is added as a value to - * the global storage. + * the global storage. The storage is not cleared first: existing + * keys will remain; keys that also occur in the JSON file are overwritten. */ bool load( const QString& filename ); @@ -68,7 +111,10 @@ public: */ bool saveYaml( const QString& filename ); - /// @brief reads settings from the given filename + /** @brief reads settings from the given filename + * + * See also load(), above. + */ bool loadYaml( const QString& filename ); /** @brief Get internal mapping as a constant object @@ -80,12 +126,41 @@ public: const QVariantMap& data() const { return m; } public Q_SLOTS: + /** @brief Does the store contain the given key? + * + * This can distinguish an explicitly-inserted QVariant() from + * a no-value-exists QVariant. See value() for details. + */ bool contains( const QString& key ) const; + /** @brief The number of keys in the store + * + * This should be unsigned, but the underlying QMap uses signed as well. + * Equal to keys().length(), in theory. + */ int count() const; + /** @brief The keys in the store + * + * This makes a copy of all the keys currently in the store, which + * could be used for iterating over all the values in the store. + */ QStringList keys() const; + /** @brief Gets a value from the store + * + * If a value has been previously inserted, returns that value. + * If @p key does not exist in the store, returns a QVariant() + * (an invalid QVariant, which boolean-converts to false). Since + * QVariant() van also be inserted explicitly, use contains() + * to check for the presence of a key if you need that. + */ QVariant value( const QString& key ) const; signals: + /** @brief Emitted any time the store changes + * + * Also emitted sometimes when the store does not change, e.g. + * when removing a non-existent key or inserting a value that + * is already present. + */ void changed(); private: From 0121e3755b6cda034ecf759a12a7ed477617e0dc Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 00:01:20 +0200 Subject: [PATCH 54/91] [libcalamares] GS improve load/save - save should be const - rename save() to saveJson() for parity with saveYaml() --- src/libcalamares/GlobalStorage.cpp | 6 +++--- src/libcalamares/GlobalStorage.h | 6 +++--- src/modules/preservefiles/PreserveFiles.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index 519b1d0d6..a31a89b21 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -94,7 +94,7 @@ GlobalStorage::debugDump() const } bool -GlobalStorage::save( const QString& filename ) +GlobalStorage::saveJson( const QString& filename ) const { QFile f( filename ); if ( !f.open( QFile::WriteOnly ) ) @@ -108,7 +108,7 @@ GlobalStorage::save( const QString& filename ) } bool -GlobalStorage::load( const QString& filename ) +GlobalStorage::loadJson( const QString& filename ) { QFile f( filename ); if ( !f.open( QFile::ReadOnly ) ) @@ -139,7 +139,7 @@ GlobalStorage::load( const QString& filename ) } bool -GlobalStorage::saveYaml( const QString& filename ) +GlobalStorage::saveYaml( const QString& filename ) const { return CalamaresUtils::saveYaml( filename, m ); } diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h index 782d443d4..14d5d41e9 100644 --- a/src/libcalamares/GlobalStorage.h +++ b/src/libcalamares/GlobalStorage.h @@ -94,7 +94,7 @@ public: * and this JSON file will contain that password in-the-only-slightly- * obscured form. */ - bool save( const QString& filename ); + bool saveJson( const QString& filename ) const; /** @brief Adds the keys from the given JSON file * @@ -103,13 +103,13 @@ public: * the global storage. The storage is not cleared first: existing * keys will remain; keys that also occur in the JSON file are overwritten. */ - bool load( const QString& filename ); + bool loadJson( const QString& filename ); /** @brief write as YAML to the given filename * * See also save(), above. */ - bool saveYaml( const QString& filename ); + bool saveYaml( const QString& filename ) const; /** @brief reads settings from the given filename * diff --git a/src/modules/preservefiles/PreserveFiles.cpp b/src/modules/preservefiles/PreserveFiles.cpp index 8352e861d..7262630e0 100644 --- a/src/modules/preservefiles/PreserveFiles.cpp +++ b/src/modules/preservefiles/PreserveFiles.cpp @@ -138,7 +138,7 @@ PreserveFiles::exec() } if ( it.type == ItemType::Config ) { - if ( Calamares::JobQueue::instance()->globalStorage()->save( dest ) ) + if ( Calamares::JobQueue::instance()->globalStorage()->saveJson( dest ) ) { cWarning() << "Could not write config for" << dest; } From dc5d98af7d5c9c2b3ff36fa07912abf5f0331a46 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 23:27:04 +0200 Subject: [PATCH 55/91] [libcalamares] Address outdates assumptions about thread-safety --- src/libcalamares/GlobalStorage.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libcalamares/GlobalStorage.h b/src/libcalamares/GlobalStorage.h index 14d5d41e9..a12d78978 100644 --- a/src/libcalamares/GlobalStorage.h +++ b/src/libcalamares/GlobalStorage.h @@ -22,6 +22,7 @@ #ifndef CALAMARES_GLOBALSTORAGE_H #define CALAMARES_GLOBALSTORAGE_H +#include #include #include #include @@ -45,8 +46,11 @@ namespace Calamares * * In general, see QVariantMap (possibly after calling data()) for details. * - * This class is not thread-safe, but as long as JobQueue is, that's ok - * because only one module is active at a time. + * This class is thread-safe -- most accesses go through JobQueue, which + * handles threading itself, but because modules load in parallel and can + * have asynchronous tasks like GeoIP lookups, the storage itself also + * has locking. All methods are thread-safe, use data() to make a snapshot + * copy for use outside of the thread-safe API. */ class GlobalStorage : public QObject { @@ -117,13 +121,11 @@ public: */ bool loadYaml( const QString& filename ); - /** @brief Get internal mapping as a constant object + /** @brief Make a complete copy of the data * - * Note that the VariantMap underneath may change, because - * it's not constant in itself. Connect to the changed() - * signal for notifications. + * Provides a snapshot of the data at a given time. */ - const QVariantMap& data() const { return m; } + QVariantMap data() const { return m; } public Q_SLOTS: /** @brief Does the store contain the given key? @@ -164,7 +166,10 @@ signals: void changed(); private: + class ReadLock; + class WriteLock; QVariantMap m; + mutable QMutex m_mutex; }; } // namespace Calamares From ac713d8c4bdf07fb80d171a0fa1b6a58517d1c47 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 6 Aug 2020 23:48:18 +0200 Subject: [PATCH 56/91] [libcalamares] Apply locking to GS access --- src/libcalamares/GlobalStorage.cpp | 41 +++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index a31a89b21..6d3b33450 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -27,12 +27,35 @@ #include #include +#include using CalamaresUtils::operator""_MiB; namespace Calamares { +class GlobalStorage::ReadLock : public QMutexLocker +{ +public: + ReadLock( const GlobalStorage* gs ) + : QMutexLocker( &gs->m_mutex ) + { + } +}; + +class GlobalStorage::WriteLock : public QMutexLocker +{ +public: + WriteLock( GlobalStorage* gs ) + : QMutexLocker( &gs->m_mutex ) + , m_gs( gs ) + { + } + ~WriteLock() { m_gs->changed(); } + + GlobalStorage* m_gs; +}; + GlobalStorage::GlobalStorage( QObject* parent ) : QObject( parent ) { @@ -42,6 +65,7 @@ GlobalStorage::GlobalStorage( QObject* parent ) bool GlobalStorage::contains( const QString& key ) const { + ReadLock l( this ); return m.contains( key ); } @@ -49,6 +73,7 @@ GlobalStorage::contains( const QString& key ) const int GlobalStorage::count() const { + ReadLock l( this ); return m.count(); } @@ -56,14 +81,15 @@ GlobalStorage::count() const void GlobalStorage::insert( const QString& key, const QVariant& value ) { + WriteLock l( this ); m.insert( key, value ); - emit changed(); } QStringList GlobalStorage::keys() const { + ReadLock l( this ); return m.keys(); } @@ -71,8 +97,8 @@ GlobalStorage::keys() const int GlobalStorage::remove( const QString& key ) { + WriteLock l( this ); int nItems = m.remove( key ); - emit changed(); return nItems; } @@ -80,13 +106,15 @@ GlobalStorage::remove( const QString& key ) QVariant GlobalStorage::value( const QString& key ) const { + ReadLock l( this ); return m.value( key ); } void GlobalStorage::debugDump() const { - cDebug() << "GlobalStorage" << Logger::Pointer(this) << m.count() << "items"; + ReadLock l( this ); + cDebug() << "GlobalStorage" << Logger::Pointer( this ) << m.count() << "items"; for ( auto it = m.cbegin(); it != m.cend(); ++it ) { cDebug() << Logger::SubEntry << it.key() << '\t' << it.value(); @@ -96,6 +124,7 @@ GlobalStorage::debugDump() const bool GlobalStorage::saveJson( const QString& filename ) const { + ReadLock l( this ); QFile f( filename ); if ( !f.open( QFile::WriteOnly ) ) { @@ -128,6 +157,7 @@ GlobalStorage::loadJson( const QString& filename ) } else { + WriteLock l( this ); auto map = d.toVariant().toMap(); for ( auto i = map.constBegin(); i != map.constEnd(); ++i ) { @@ -141,6 +171,7 @@ GlobalStorage::loadJson( const QString& filename ) bool GlobalStorage::saveYaml( const QString& filename ) const { + ReadLock l( this ); return CalamaresUtils::saveYaml( filename, m ); } @@ -151,9 +182,11 @@ GlobalStorage::loadYaml( const QString& filename ) auto gs = CalamaresUtils::loadYaml( filename, &ok ); if ( ok ) { + WriteLock l( this ); m = gs; + return true; } - return ok; + return false; } From 3c618a9a192bcc39d26bb8ecba7795a41838c076 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 00:08:47 +0200 Subject: [PATCH 57/91] [libcalamares] Fix GS load behavior - the loadJson behavior did too many notifications, and was likely to deadlock; write directly to the map instead and emit only once. - the loadYaml method did something very different from its documentation or intent. --- src/libcalamares/GlobalStorage.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libcalamares/GlobalStorage.cpp b/src/libcalamares/GlobalStorage.cpp index 6d3b33450..253a4d6ad 100644 --- a/src/libcalamares/GlobalStorage.cpp +++ b/src/libcalamares/GlobalStorage.cpp @@ -158,10 +158,13 @@ GlobalStorage::loadJson( const QString& filename ) else { WriteLock l( this ); + // Do **not** use method insert() here, because it would + // recursively lock the mutex, leading to deadlock. Also, + // that would emit changed() for each key. auto map = d.toVariant().toMap(); for ( auto i = map.constBegin(); i != map.constEnd(); ++i ) { - insert( i.key(), *i ); + m.insert( i.key(), *i ); } return true; } @@ -179,11 +182,17 @@ bool GlobalStorage::loadYaml( const QString& filename ) { bool ok = false; - auto gs = CalamaresUtils::loadYaml( filename, &ok ); + auto map = CalamaresUtils::loadYaml( filename, &ok ); if ( ok ) { WriteLock l( this ); - m = gs; + // Do **not** use method insert() here, because it would + // recursively lock the mutex, leading to deadlock. Also, + // that would emit changed() for each key. + for ( auto i = map.constBegin(); i != map.constEnd(); ++i ) + { + m.insert( i.key(), *i ); + } return true; } return false; From a44e6802e5a296f3b5d53a7ebd0d626b8d087d62 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 00:13:58 +0200 Subject: [PATCH 58/91] [libcalamares] Rename tests for consistency --- src/libcalamares/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 8e209f8a3..fef0b157a 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -216,19 +216,19 @@ endforeach() # # calamares_add_test( - libcalamarestest + libcalamaresutilstest SOURCES utils/Tests.cpp ) calamares_add_test( - libcalamarestestpaths + libcalamaresutilspathstest SOURCES utils/TestPaths.cpp ) calamares_add_test( - geoiptest + libcalamaresgeoiptest SOURCES geoip/GeoIPTests.cpp ${geoip_src} From dbc49f001eccb6882c4ab4b4ad8d8186a7fd7664 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 00:45:36 +0200 Subject: [PATCH 59/91] [libcalamares] Test GS - test insert, remove, emitted signals - test loading and saving of YAML and JSON This shows up a big bug in the YAML saving code (which was never used, it seems, anyway) --- src/libcalamares/CMakeLists.txt | 15 +++-- src/libcalamares/Tests.cpp | 115 ++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 src/libcalamares/Tests.cpp diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index fef0b157a..ff341bb56 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -1,6 +1,7 @@ # === This file is part of Calamares - === # # SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later # # Calamares is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,12 +16,12 @@ # You should have received a copy of the GNU General Public License # along with Calamares. If not, see . # -# SPDX-License-Identifier: GPL-3.0-or-later -# License-Filename: LICENSE -# + # # libcalamares is the non-GUI part of Calamares, which includes handling -# translations, configurations, logging, utilities, global storage, and (non-GUI) jobs. +# translations, configurations, logging, utilities, global storage, and +# (non-GUI) jobs. +# add_definitions( -DDLLEXPORT_PRO ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) @@ -215,6 +216,12 @@ endforeach() ### TESTING # # +calamares_add_test( + libcalamarestest + SOURCES + Tests.cpp +) + calamares_add_test( libcalamaresutilstest SOURCES diff --git a/src/libcalamares/Tests.cpp b/src/libcalamares/Tests.cpp new file mode 100644 index 000000000..744e56113 --- /dev/null +++ b/src/libcalamares/Tests.cpp @@ -0,0 +1,115 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + */ + +#include "GlobalStorage.h" + +#include +#include +#include + +class TestLibCalamares : public QObject +{ + Q_OBJECT +public: + TestLibCalamares() {} + virtual ~TestLibCalamares() {} + +private Q_SLOTS: + void testGSModify(); + void testGSLoadSave(); +}; + +void +TestLibCalamares::testGSModify() +{ + Calamares::GlobalStorage gs; + QSignalSpy spy( &gs, &Calamares::GlobalStorage::changed ); + + const QString key( "derp" ); + + QCOMPARE( gs.count(), 0 ); + QVERIFY( !gs.contains( key ) ); + + const int value = 17; + gs.insert( key, value ); + QCOMPARE( gs.count(), 1 ); + QVERIFY( gs.contains( key ) ); + QCOMPARE( gs.value( key ).type(), QVariant::Int ); + QCOMPARE( gs.value( key ).toString(), QString( "17" ) ); // It isn't a string, but does convert + QCOMPARE( gs.value( key ).toInt(), value ); + + gs.remove( key ); + QCOMPARE( gs.count(), 0 ); + QVERIFY( !gs.contains( key ) ); + + QCOMPARE( spy.count(), 2 ); // one insert, one remove +} + +void +TestLibCalamares::testGSLoadSave() +{ + Calamares::GlobalStorage gs; + const QString jsonfilename( "gs.test.json" ); + const QString yamlfilename( "gs.test.yaml" ); + + gs.insert( "derp", 17 ); + gs.insert( "cow", "moo" ); + gs.insert( "dwarfs", QStringList { "dopey", "sneezy" } ); + + QCOMPARE( gs.count(), 3 ); + + QVERIFY( gs.saveJson( jsonfilename ) ); + Calamares::GlobalStorage gs2; + QCOMPARE( gs2.count(), 0 ); + QVERIFY( gs2.loadJson( jsonfilename ) ); + QCOMPARE( gs2.count(), 3 ); + QCOMPARE( gs2.data(), gs.data() ); + + QVERIFY( gs.saveYaml( yamlfilename ) ); + Calamares::GlobalStorage gs3; + QCOMPARE( gs3.count(), 0 ); + QVERIFY( gs3.loadYaml( jsonfilename ) ); + QCOMPARE( gs3.count(), 3 ); + QCOMPARE( gs3.data(), gs.data() ); + + // Failures in loading + QVERIFY( !gs3.loadYaml( jsonfilename ) ); + QVERIFY( !gs3.loadJson( yamlfilename ) ); + + Calamares::GlobalStorage gs4; + gs4.insert( "derp", 32 ); + gs4.insert( "dorp", "Varsseveld" ); + QCOMPARE( gs4.count(), 2 ); + QVERIFY( gs4.contains( "dorp" ) ); + QCOMPARE( gs4.value( "derp" ).toInt(), 32 ); + QVERIFY( gs4.loadJson( jsonfilename ) ); + // 3 keys from the file, but one overwrite + QCOMPARE( gs4.count(), 4 ); + QVERIFY( gs4.contains( "dorp" ) ); + QCOMPARE( gs4.value( "derp" ).toInt(), 17 ); // This one was overwritten +} + + +QTEST_GUILESS_MAIN( TestLibCalamares ) + +#include "utils/moc-warnings.h" + +#include "Tests.moc" From 0de98fe4c125098c7e9325fbc4367ab483612e9f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 00:57:30 +0200 Subject: [PATCH 60/91] [libcalamares] Expand YAML testing a little - load/save of a stringlist seems to work --- src/libcalamares/testdata/yaml-list.conf | 8 ++++++++ src/libcalamares/utils/Tests.cpp | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/libcalamares/testdata/yaml-list.conf diff --git a/src/libcalamares/testdata/yaml-list.conf b/src/libcalamares/testdata/yaml-list.conf new file mode 100644 index 000000000..d6992b1fb --- /dev/null +++ b/src/libcalamares/testdata/yaml-list.conf @@ -0,0 +1,8 @@ +# YAML dump +--- +"cow": "moo" +"derp": 17 +"dwarfs": + - "sleepy" + - "sneezy" + - "doc" diff --git a/src/libcalamares/utils/Tests.cpp b/src/libcalamares/utils/Tests.cpp index 300b9b531..5a01848bf 100644 --- a/src/libcalamares/utils/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -148,6 +148,7 @@ LibCalamaresTests::recursiveCompareMap( const QVariantMap& a, const QVariantMap& void LibCalamaresTests::testLoadSaveYamlExtended() { + Logger::setupLogLevel( Logger::LOGDEBUG ); bool loaded_ok; for ( const auto& confname : findConf( QDir( "../src" ) ) ) { From cb20ba6aba632b85302379511b0d5976def995fc Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 01:11:14 +0200 Subject: [PATCH 61/91] [libcalamares] More GS load/save testing - failures elsewhere boil down to QStringList is not supported, but a QVariantList of QVariants of QStrings is. --- src/libcalamares/Tests.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libcalamares/Tests.cpp b/src/libcalamares/Tests.cpp index 744e56113..bf419d721 100644 --- a/src/libcalamares/Tests.cpp +++ b/src/libcalamares/Tests.cpp @@ -21,6 +21,8 @@ #include "GlobalStorage.h" +#include "utils/Logger.h" + #include #include #include @@ -35,6 +37,7 @@ public: private Q_SLOTS: void testGSModify(); void testGSLoadSave(); + void testGSLoadSave2(); }; void @@ -107,6 +110,35 @@ TestLibCalamares::testGSLoadSave() QCOMPARE( gs4.value( "derp" ).toInt(), 17 ); // This one was overwritten } +void +TestLibCalamares::testGSLoadSave2() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); + + const QString filename( "../src/libcalamares/testdata/yaml-list.conf" ); + if ( !QFile::exists( filename ) ) + { + return; + } + + Calamares::GlobalStorage gs1; + const QString key( "dwarfs" ); + + QVERIFY( gs1.loadYaml( filename ) ); + QCOMPARE( gs1.count(), 3 ); // From examining the file + QVERIFY( gs1.contains( key ) ); + cDebug() << gs1.value( key ).type() << gs1.value( key ); + QCOMPARE( gs1.value( key ).type(), QVariant::List ); + + const QString yamlfilename( "gs.test.yaml" ); + QVERIFY( gs1.saveYaml( yamlfilename ) ); + + Calamares::GlobalStorage gs2; + QVERIFY( gs2.loadYaml( yamlfilename ) ); + QVERIFY( gs2.contains( key ) ); + QCOMPARE( gs2.value( key ).type(), QVariant::List ); +} + QTEST_GUILESS_MAIN( TestLibCalamares ) From f324a055e502f45f44175578514a29dc5ca37bd2 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 10:43:29 +0200 Subject: [PATCH 62/91] CMake: put completions with the other "misc. installs" --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fcebae6e..10f314856 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -455,14 +455,6 @@ list( SORT CALAMARES_TRANSLATION_LANGUAGES ) add_subdirectory( lang ) # i18n tools -if ( INSTALL_COMPLETION ) - if( NOT CMAKE_INSTALL_BASHCOMPLETIONDIR ) - set( CMAKE_INSTALL_BASHCOMPLETIONDIR "${CMAKE_INSTALL_DATADIR}/bash-completion/completions" ) - endif() - - install( FILES ${CMAKE_SOURCE_DIR}/data/completion/bash/calamares DESTINATION "${CMAKE_INSTALL_BASHCOMPLETIONDIR}" ) -endif() - ### Example Distro # # For testing purposes Calamares includes a very, very, limited sample @@ -652,6 +644,14 @@ if( INSTALL_POLKIT ) ) endif() +if ( INSTALL_COMPLETION ) + if( NOT CMAKE_INSTALL_BASHCOMPLETIONDIR ) + set( CMAKE_INSTALL_BASHCOMPLETIONDIR "${CMAKE_INSTALL_DATADIR}/bash-completion/completions" ) + endif() + + install( FILES ${CMAKE_SOURCE_DIR}/data/completion/bash/calamares DESTINATION "${CMAKE_INSTALL_BASHCOMPLETIONDIR}" ) +endif() + install( FILES calamares.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications From 463ea3c73fac6fd8dd8fd239e660b222fc21d65e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 10:50:33 +0200 Subject: [PATCH 63/91] [displaymanager] Fix config schema --- .../displaymanager/displaymanager.schema.yaml | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/modules/displaymanager/displaymanager.schema.yaml b/src/modules/displaymanager/displaymanager.schema.yaml index a16af732f..08923f726 100644 --- a/src/modules/displaymanager/displaymanager.schema.yaml +++ b/src/modules/displaymanager/displaymanager.schema.yaml @@ -4,13 +4,17 @@ $id: https://calamares.io/schemas/displaymanager additionalProperties: false type: object properties: - "displaymanagers": - type: seq - sequence: - - { type: string, required: true, enum: [slim, sddm, lightdm, gdm, mdm, lxdm, kdm] } - "defaultDesktopEnvironment": - type: map - mapping: - "executable": { type: str } - "desktopFile": { type: str } - "basicSetup": { type: boolean, default: false } + displaymanagers: + type: array + items: + type: string + enum: [slim, sddm, lightdm, gdm, mdm, lxdm, kdm] + minItems: 1 # Must be non-empty, if present at all + defaultDesktopEnvironment: + type: object + properties: + executable: { type: string } + desktopFile: { type: string } + required: [ executable, desktopFile ] + basicSetup: { type: boolean, default: false } + sysconfigSetup: { type: boolean, default: false } From 3fc23e3b07da9c4036d24fb9aee77830e4319664 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 11:43:48 +0200 Subject: [PATCH 64/91] [grubcfg] Fix config schema --- src/modules/grubcfg/grubcfg.schema.yaml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/modules/grubcfg/grubcfg.schema.yaml b/src/modules/grubcfg/grubcfg.schema.yaml index 10aa34c2b..5121e2be7 100644 --- a/src/modules/grubcfg/grubcfg.schema.yaml +++ b/src/modules/grubcfg/grubcfg.schema.yaml @@ -4,12 +4,14 @@ $id: https://calamares.io/schemas/grubcfg additionalProperties: false type: object properties: - "overwrite": { type: boolean, default: false } - "defaults": - type: map - mapping: - "GRUB_TIMEOUT": { type: int, required: true } - "GRUB_DEFAULT": { type: string, required: true } - "GRUB_DISABLE_SUBMENU": { type: boolean, default: true } - "GRUB_TERMINAL_OUTPUT": { type: string, required: true } - "GRUB_DISABLE_RECOVERY": { type: boolean, default: true } + overwrite: { type: boolean, default: false } + defaults: + type: object + additionalProperties: true # Other fields are acceptable + properties: + GRUB_TIMEOUT: { type: integer } + GRUB_DEFAULT: { type: string } + GRUB_DISABLE_SUBMENU: { type: boolean, default: true } + GRUB_TERMINAL_OUTPUT: { type: string } + GRUB_DISABLE_RECOVERY: { type: boolean, default: true } + required: [ GRUB_TIMEOUT, GRUB_DEFAULT, GRUB_TERMINAL_OUTPUT ] From f85c70d4d2c731dc66bae4a0cc42e4946f2330b0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 11:55:16 +0200 Subject: [PATCH 65/91] [grubcfg] Introduce prefer_grub_d - new setting for using /etc/defaults/grub.d/ (SEE #1457), not implemented - add missing fields to schema for config file --- src/modules/grubcfg/grubcfg.conf | 5 +++++ src/modules/grubcfg/grubcfg.schema.yaml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/modules/grubcfg/grubcfg.conf b/src/modules/grubcfg/grubcfg.conf index ba31d6070..374561787 100644 --- a/src/modules/grubcfg/grubcfg.conf +++ b/src/modules/grubcfg/grubcfg.conf @@ -17,6 +17,11 @@ # already existed. If set to false, edits the existing file instead. overwrite: false +# If set to true, prefer to write files in /etc/default/grub.d/ +# rather than the single file /etc/default/grub. If this is set, +# Calamares will write /etc/default/grub.d/00Calamares instead. +prefer_grub_d: false + # If set to true, an **existing** setting for GRUB_DISTRIBUTOR is # kept, not updated to the *bootloaderEntryName* from the branding file. # Use this if the GRUB_DISTRIBUTOR setting in the file is "smart" in diff --git a/src/modules/grubcfg/grubcfg.schema.yaml b/src/modules/grubcfg/grubcfg.schema.yaml index 5121e2be7..f010ac694 100644 --- a/src/modules/grubcfg/grubcfg.schema.yaml +++ b/src/modules/grubcfg/grubcfg.schema.yaml @@ -5,6 +5,8 @@ additionalProperties: false type: object properties: overwrite: { type: boolean, default: false } + keepDistributor: { type: boolean, default: false } + prefer_grub_d: { type: boolean, default: false } defaults: type: object additionalProperties: true # Other fields are acceptable From aa50dfb8a12a349d7fdd4172f38ad2925e676e82 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 12:02:42 +0200 Subject: [PATCH 66/91] [grubcfg] refactor finding-the-grub-paths into a function --- src/modules/grubcfg/main.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py index 77a668960..bf1ec4b9f 100644 --- a/src/modules/grubcfg/main.py +++ b/src/modules/grubcfg/main.py @@ -37,6 +37,19 @@ def pretty_name(): return _("Configure GRUB.") +def get_grub_config_paths(root_mount_point): + """ + Figures out where to put the grub config files. Returns + a directory path and the full path of a file inside that + directory, as "the grub-directory" and "the config file". + + Returns a path into @p root_mount_point. + """ + default_dir = os.path.join(root_mount_point, "etc/default") + default_grub = os.path.join(default_dir, "grub") + + return (default_dir, default_grub) + def modify_grub_default(partitions, root_mount_point, distributor): """ Configures '/etc/default/grub' for hibernation and plymouth. @@ -54,8 +67,7 @@ def modify_grub_default(partitions, root_mount_point, distributor): is always updated to set this value. :return: """ - default_dir = os.path.join(root_mount_point, "etc/default") - default_grub = os.path.join(default_dir, "grub") + default_dir, default_grub = get_grub_config_paths(root_mount_point) distributor_replace = distributor.replace("'", "'\\''") dracut_bin = libcalamares.utils.target_env_call( ["sh", "-c", "which dracut"] From 064fff0c12067a370fbbcc64c6d1c0efb0ba4df0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 12:05:46 +0200 Subject: [PATCH 67/91] [grubcfg] Drop default_dir - the default_dir was only stored in modify_grub_default() to create the directory if needed; move that functionality to the get_grub_config_paths() function (and drop the "s", since it now returns just one). --- src/modules/grubcfg/main.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py index bf1ec4b9f..3c3f3d447 100644 --- a/src/modules/grubcfg/main.py +++ b/src/modules/grubcfg/main.py @@ -37,18 +37,21 @@ def pretty_name(): return _("Configure GRUB.") -def get_grub_config_paths(root_mount_point): +def get_grub_config_path(root_mount_point): """ Figures out where to put the grub config files. Returns - a directory path and the full path of a file inside that - directory, as "the grub-directory" and "the config file". + a the full path of a file inside that + directory, as "the config file". Returns a path into @p root_mount_point. """ default_dir = os.path.join(root_mount_point, "etc/default") default_grub = os.path.join(default_dir, "grub") - return (default_dir, default_grub) + if not os.path.exists(default_dir): + os.mkdir(default_dir) + + return default_grub def modify_grub_default(partitions, root_mount_point, distributor): """ @@ -67,7 +70,7 @@ def modify_grub_default(partitions, root_mount_point, distributor): is always updated to set this value. :return: """ - default_dir, default_grub = get_grub_config_paths(root_mount_point) + default_grub = get_grub_config_path(root_mount_point) distributor_replace = distributor.replace("'", "'\\''") dracut_bin = libcalamares.utils.target_env_call( ["sh", "-c", "which dracut"] @@ -154,9 +157,6 @@ def modify_grub_default(partitions, root_mount_point, distributor): distributor_line = "GRUB_DISTRIBUTOR='{!s}'".format(distributor_replace) - if not os.path.exists(default_dir): - os.mkdir(default_dir) - have_kernel_cmd = False have_distributor_line = False From 8bf95b68812ada29ba82bddf67a0f243ae97cd42 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 12:13:51 +0200 Subject: [PATCH 68/91] [grubcfg] Support prefer_grub_d settings --- src/modules/grubcfg/main.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py index 3c3f3d447..04b5eba9e 100644 --- a/src/modules/grubcfg/main.py +++ b/src/modules/grubcfg/main.py @@ -46,12 +46,19 @@ def get_grub_config_path(root_mount_point): Returns a path into @p root_mount_point. """ default_dir = os.path.join(root_mount_point, "etc/default") - default_grub = os.path.join(default_dir, "grub") + default_config_file = "grub" + + if "prefer_grub_d" in libcalamares.job.configuration and libcalamares.job.configuration["prefer_grub_d"]: + possible_dir = os.path.join(root_mount_point, "etc/default/grub.d") + if os.path.exists(possible_dir) and os.path.isdir(possible_dir): + default_dir = possible_dir + default_config_file = "00calamares" if not os.path.exists(default_dir): os.mkdir(default_dir) - return default_grub + return os.path.join(default_dir, default_config_file) + def modify_grub_default(partitions, root_mount_point, distributor): """ From 473daecdbfe659cec0e307b341636a8a76c1d4da Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 7 Aug 2020 13:42:47 +0200 Subject: [PATCH 69/91] [grubcfg] expand config-testing for the new features - create directories for new tests ahead of the tests themselves; this **can** still cause problems if a test is run standalone. - if creating the grub-dir at runtime is necessary, be informative if it fails. --- src/modules/grubcfg/main.py | 6 +++++- src/modules/grubcfg/tests/2.global | 2 +- src/modules/grubcfg/tests/3.global | 10 ++++++++++ src/modules/grubcfg/tests/3.job | 10 ++++++++++ src/modules/grubcfg/tests/4.global | 10 ++++++++++ src/modules/grubcfg/tests/4.job | 10 ++++++++++ src/modules/grubcfg/tests/CMakeTests.txt | 10 +++++++--- 7 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/modules/grubcfg/tests/3.global create mode 100644 src/modules/grubcfg/tests/3.job create mode 100644 src/modules/grubcfg/tests/4.global create mode 100644 src/modules/grubcfg/tests/4.job diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py index 04b5eba9e..fad6d3677 100644 --- a/src/modules/grubcfg/main.py +++ b/src/modules/grubcfg/main.py @@ -55,7 +55,11 @@ def get_grub_config_path(root_mount_point): default_config_file = "00calamares" if not os.path.exists(default_dir): - os.mkdir(default_dir) + try: + os.mkdir(default_dir) + except: + libcalamares.utils.debug("Failed to create '%r'" % default_dir) + raise return os.path.join(default_dir, default_config_file) diff --git a/src/modules/grubcfg/tests/2.global b/src/modules/grubcfg/tests/2.global index 83e79db28..88f789630 100644 --- a/src/modules/grubcfg/tests/2.global +++ b/src/modules/grubcfg/tests/2.global @@ -2,7 +2,7 @@ bogus: true firmwareType: bios bootLoader: grub -rootMountPoint: /tmp/calamares +rootMountPoint: /tmp/calamares/grubcfg-test-2 branding: bootloaderEntryName: generic diff --git a/src/modules/grubcfg/tests/3.global b/src/modules/grubcfg/tests/3.global new file mode 100644 index 000000000..45468d4df --- /dev/null +++ b/src/modules/grubcfg/tests/3.global @@ -0,0 +1,10 @@ +--- +bogus: true +firmwareType: bios +bootLoader: grub +rootMountPoint: /tmp/calamares/grubcfg-test-3 + +branding: + bootloaderEntryName: generic +partitions: [] + diff --git a/src/modules/grubcfg/tests/3.job b/src/modules/grubcfg/tests/3.job new file mode 100644 index 000000000..7182deeb2 --- /dev/null +++ b/src/modules/grubcfg/tests/3.job @@ -0,0 +1,10 @@ +--- +overwrite: true +prefer_grub_d: true # But it doesn't exist +keepDistributor: false +defaults: + GRUB_TIMEOUT: 5 + GRUB_DEFAULT: "saved" + GRUB_DISABLE_SUBMENU: true + GRUB_TERMINAL_OUTPUT: "console" + GRUB_DISABLE_RECOVERY: true diff --git a/src/modules/grubcfg/tests/4.global b/src/modules/grubcfg/tests/4.global new file mode 100644 index 000000000..fe24ba6ca --- /dev/null +++ b/src/modules/grubcfg/tests/4.global @@ -0,0 +1,10 @@ +--- +bogus: true +firmwareType: bios +bootLoader: grub +rootMountPoint: /tmp/calamares/grubcfg-test-4 + +branding: + bootloaderEntryName: generic +partitions: [] + diff --git a/src/modules/grubcfg/tests/4.job b/src/modules/grubcfg/tests/4.job new file mode 100644 index 000000000..f8f30f21b --- /dev/null +++ b/src/modules/grubcfg/tests/4.job @@ -0,0 +1,10 @@ +--- +overwrite: true +prefer_grub_d: true +keepDistributor: false +defaults: + GRUB_TIMEOUT: 5 + GRUB_DEFAULT: "saved" + GRUB_DISABLE_SUBMENU: true + GRUB_TERMINAL_OUTPUT: "console" + GRUB_DISABLE_RECOVERY: true diff --git a/src/modules/grubcfg/tests/CMakeTests.txt b/src/modules/grubcfg/tests/CMakeTests.txt index 299fccf07..78a0f85ac 100644 --- a/src/modules/grubcfg/tests/CMakeTests.txt +++ b/src/modules/grubcfg/tests/CMakeTests.txt @@ -2,11 +2,15 @@ # - 2.global specifies /tmp/calamares as the rootMountPath, # so we end up editing files there. Create the directory # beforehand, so the test doesn't blow up. -set(_grub_root /tmp/calamares/etc/default) -set(_grub_file ${_grub_root}/bogus) add_test( NAME make-grubcfg-dirs - COMMAND ${CMAKE_COMMAND} -E make_directory ${_grub_root} + COMMAND ${CMAKE_COMMAND} -E make_directory + /tmp/calamares/grubcfg-test-2/etc/default + /tmp/calamares/grubcfg-test-3/etc/default + /tmp/calamares/grubcfg-test-4/etc/default/grub.d ) set_tests_properties(load-grubcfg-2 PROPERTIES DEPENDS make-grubcfg-dirs) +set_tests_properties(load-grubcfg-3 PROPERTIES DEPENDS make-grubcfg-dirs) +set_tests_properties(load-grubcfg-4 PROPERTIES DEPENDS make-grubcfg-dirs) + From 0fda1dcf7df7e7a18fcfc2e7f2a2f1e83a3efd95 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 13:26:39 +0200 Subject: [PATCH 70/91] [libcalamares] Refactor finding-TZ algorithm - introduce a distance function and use that, rather than coding it inside the find() function. This is prep-work for unifying the find() calls, based on various coordinate systems. --- src/libcalamares/locale/TimeZone.cpp | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index e2779281f..6eabef6e8 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -336,6 +336,24 @@ ZonesModel::find( const QString& region, const QString& zone ) const return nullptr; } +STATICTEST const TimeZoneData* +find( const ZoneVector& zones, std::function< double( const TimeZoneData* ) > distance ) +{ + double smallestDistance = 1000000.0; + const TimeZoneData* closest = nullptr; + + for ( const auto* zone : zones ) + { + double thisDistance = distance( zone ); + if ( thisDistance < smallestDistance ) + { + closest = zone; + smallestDistance = thisDistance; + } + } + return closest; +} + const TimeZoneData* ZonesModel::find( double latitude, double longitude ) const { @@ -344,12 +362,7 @@ ZonesModel::find( double latitude, double longitude ) const * either N/S or E/W equal to any other; this obviously * falls apart at the poles. */ - - double largestDifference = 720.0; - const TimeZoneData* closest = nullptr; - - for ( const auto* zone : m_private->m_zones ) - { + auto distance = [&]( const TimeZoneData* zone ) -> double { // Latitude doesn't wrap around: there is nothing north of 90 double latitudeDifference = abs( zone->latitude() - latitude ); @@ -368,13 +381,10 @@ ZonesModel::find( double latitude, double longitude ) const longitudeDifference = abs( westerly - easterly ); } - if ( latitudeDifference + longitudeDifference < largestDifference ) - { - largestDifference = latitudeDifference + longitudeDifference; - closest = zone; - } - } - return closest; + return latitudeDifference + longitudeDifference; + }; + + return CalamaresUtils::Locale::find( m_private->m_zones, distance ); } QObject* From 2f871acbfd3c8def44d8e54887d6c5357259dc1e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 13:45:32 +0200 Subject: [PATCH 71/91] [libcalamares] Expose distanceFunc-find for timezones --- src/libcalamares/locale/TimeZone.cpp | 12 +++++++++--- src/libcalamares/locale/TimeZone.h | 16 +++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 6eabef6e8..4c641cd17 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -337,14 +337,14 @@ ZonesModel::find( const QString& region, const QString& zone ) const } STATICTEST const TimeZoneData* -find( const ZoneVector& zones, std::function< double( const TimeZoneData* ) > distance ) +find( const ZoneVector& zones, const std::function< double( const TimeZoneData* ) >& distanceFunc ) { double smallestDistance = 1000000.0; const TimeZoneData* closest = nullptr; for ( const auto* zone : zones ) { - double thisDistance = distance( zone ); + double thisDistance = distanceFunc( zone ); if ( thisDistance < smallestDistance ) { closest = zone; @@ -354,6 +354,12 @@ find( const ZoneVector& zones, std::function< double( const TimeZoneData* ) > di return closest; } +const TimeZoneData* +ZonesModel::find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const +{ + return CalamaresUtils::Locale::find( m_private->m_zones, distanceFunc ); +} + const TimeZoneData* ZonesModel::find( double latitude, double longitude ) const { @@ -384,7 +390,7 @@ ZonesModel::find( double latitude, double longitude ) const return latitudeDifference + longitudeDifference; }; - return CalamaresUtils::Locale::find( m_private->m_zones, distance ); + return find( distance ); } QObject* diff --git a/src/libcalamares/locale/TimeZone.h b/src/libcalamares/locale/TimeZone.h index 1d4b4a8ff..cc0c35ea2 100644 --- a/src/libcalamares/locale/TimeZone.h +++ b/src/libcalamares/locale/TimeZone.h @@ -167,6 +167,17 @@ public: Iterator begin() const { return Iterator( m_private ); } + /** @brief Look up TZ data based on an arbitrary distance function + * + * This is a generic method that can define distance in whatever + * coordinate system is wanted; returns the zone with the smallest + * distance. The @p distanceFunc must return "the distance" for + * each zone. It would be polite to return something non-negative. + * + * Note: not a slot, because the parameter isn't moc-able. + */ + const TimeZoneData* find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const; + public Q_SLOTS: /** @brief Look up TZ data based on its name. * @@ -176,7 +187,10 @@ public Q_SLOTS: /** @brief Look up TZ data based on the location. * - * Returns the nearest zone to the given lat and lon. + * Returns the nearest zone to the given lat and lon. This is a + * convenience function for calling find(), below, with a standard + * distance function based on the distance between the given + * location (lat and lon) and each zone's given location. */ const TimeZoneData* find( double latitude, double longitude ) const; From 0948963d86c6fdd7c3272da88dd10884cf05e525 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 22:01:10 +1000 Subject: [PATCH 72/91] [locale] Port TZ widget lookup to new find() method - The TZ widget uses a different coordinate system (mapping lat and lon to pixel locations, and then calculating Manhattan distance from that), so needs a different distance function. - Simplify code: there's just one "closest TZ" function. --- .../locale/timezonewidget/timezonewidget.cpp | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/modules/locale/timezonewidget/timezonewidget.cpp b/src/modules/locale/timezonewidget/timezonewidget.cpp index b1d3cfeaa..778f0535d 100644 --- a/src/modules/locale/timezonewidget/timezonewidget.cpp +++ b/src/modules/locale/timezonewidget/timezonewidget.cpp @@ -190,28 +190,15 @@ 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 TimeZoneData* closest = nullptr; - for ( auto it = m_zonesData->begin(); it; ++it ) - { - const auto* zone = *it; - if ( zone ) - { - 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(); - } - } - } + int mX = event->pos().x(); + int mY = event->pos().y(); + auto distance = [&]( const CalamaresUtils::Locale::TimeZoneData* zone ) { + QPoint locPos = TimeZoneImageList::getLocationPosition( zone->longitude(), zone->latitude() ); + return double( abs( mX - locPos.x() ) + abs( mY - locPos.y() ) ); + }; + const auto* closest = m_zonesData->find( distance ); if ( closest ) { // Set zone image and repaint widget From 00626fd96c9341d2a0a1858481d20522a6593067 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 22:12:22 +1000 Subject: [PATCH 73/91] [libcalamares] Refactor timezone loading - load from a TextStream. This is prep-work for alternate TZ data sources. --- src/libcalamares/locale/TimeZone.cpp | 144 ++++++++++++++------------- 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 4c641cd17..4cd2bfb9a 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -109,6 +109,80 @@ RegionData::tr() const return QObject::tr( m_human, "tz_regions" ); } +static void +loadTZData( RegionVector& regions, ZoneVector& zones, QTextStream& in ) +{ + 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; + } + + QString countryCode = list.at( 0 ).trimmed(); + if ( countryCode.size() != 2 ) + { + continue; + } + + timezoneParts.removeFirst(); + QString zone = timezoneParts.join( '/' ); + if ( zone.length() < 2 ) + { + continue; + } + + QString position = list.at( 1 ); + int cooSplitPos = position.indexOf( QRegExp( "[-+]" ), 1 ); + double latitude; + double longitude; + if ( cooSplitPos > 0 ) + { + latitude = getRightGeoLocation( position.mid( 0, cooSplitPos ) ); + longitude = getRightGeoLocation( position.mid( cooSplitPos ) ); + } + else + { + continue; + } + + // Now we have region, zone, country, lat and longitude + const RegionData* existingRegion = nullptr; + for ( const auto* p : regions ) + { + if ( p->key() == region ) + { + existingRegion = p; + break; + } + } + if ( !existingRegion ) + { + regions.append( new RegionData( region ) ); + } + zones.append( new TimeZoneData( region, zone, countryCode, latitude, longitude ) ); + } +} + static void loadTZData( RegionVector& regions, ZoneVector& zones ) { @@ -116,75 +190,7 @@ loadTZData( RegionVector& regions, ZoneVector& zones ) if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { 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; - } - - QString countryCode = list.at( 0 ).trimmed(); - if ( countryCode.size() != 2 ) - { - continue; - } - - timezoneParts.removeFirst(); - QString zone = timezoneParts.join( '/' ); - if ( zone.length() < 2 ) - { - continue; - } - - QString position = list.at( 1 ); - int cooSplitPos = position.indexOf( QRegExp( "[-+]" ), 1 ); - double latitude; - double longitude; - if ( cooSplitPos > 0 ) - { - latitude = getRightGeoLocation( position.mid( 0, cooSplitPos ) ); - longitude = getRightGeoLocation( position.mid( cooSplitPos ) ); - } - else - { - continue; - } - - // Now we have region, zone, country, lat and longitude - const RegionData* existingRegion = nullptr; - for ( const auto* p : regions ) - { - if ( p->key() == region ) - { - existingRegion = p; - break; - } - } - if ( !existingRegion ) - { - regions.append( new RegionData( region ) ); - } - zones.append( new TimeZoneData( region, zone, countryCode, latitude, longitude ) ); - } + loadTZData( regions, zones, in ); } } From 6a33e72b58a8412a9c6db5474f6eb6ad6c9495be Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 23:14:20 +1000 Subject: [PATCH 74/91] [libcalamares] Refactor test to be data-driven - this test is going to get a lot more cases, so prepare for that --- src/libcalamares/locale/Tests.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index e498ac039..d73331d44 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -52,6 +52,7 @@ private Q_SLOTS: void testComplexZones(); void testTZLookup(); void testTZIterator(); + void testLocationLookup_data(); void testLocationLookup(); }; @@ -390,24 +391,31 @@ LocaleTests::testTZIterator() QCOMPARE( ( *zones.begin() )->zone(), QStringLiteral( "Abidjan" ) ); } +void +LocaleTests::testLocationLookup_data() +{ + QTest::addColumn< double >( "latitude" ); + QTest::addColumn< double >( "longitude" ); + QTest::addColumn< QString >( "name" ); + + QTest::newRow( "London" ) << 50.0 << 0.0 << QString( "London" ); + QTest::newRow( "Tarawa E" ) << 0.0 << 179.0 << QString( "Tarawa" ); + QTest::newRow( "Tarawa W" ) << 0.0 << -179.0 << QString( "Tarawa" ); + +} + void LocaleTests::testLocationLookup() { const CalamaresUtils::Locale::ZonesModel zones; - const auto* zone = zones.find( 50.0, 0.0 ); - QVERIFY( zone ); - QCOMPARE( zone->zone(), QStringLiteral( "London" ) ); + QFETCH( double, latitude ); + QFETCH( double, longitude ); + QFETCH( QString, name ); - - // Tarawa is close to "the other side of the world" from London - zone = zones.find( 0.0, 179.0 ); + const auto* zone = zones.find( latitude, longitude ); QVERIFY( zone ); - QCOMPARE( zone->zone(), QStringLiteral( "Tarawa" ) ); - - zone = zones.find( 0.0, -179.0 ); - QVERIFY( zone ); - QCOMPARE( zone->zone(), QStringLiteral( "Tarawa" ) ); + QCOMPARE( zone->zone(), name ); } From 028d424c73bbe129128b5314a83698bb076b70d7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 23:40:13 +1000 Subject: [PATCH 75/91] [libcalamares] Expand testing of TZ location lookup - Cape Town is in South Africa, so one might expect it to get South Africa's timezone -- which is Africa/Johannesburg -- but Windhoek is closer, so it gets that. - Port Elisabeth is similar: Maseru lies between it an Johannesburg, so it gets the wrong timezone, too. These both illustrate how the limited resolution of the map, together with the "closest location" lookup, can give poor results. For most of South Africa, the "wrong" timezone is closer than the right one. --- src/libcalamares/locale/Tests.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index d73331d44..d9bfb9133 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -402,6 +402,11 @@ LocaleTests::testLocationLookup_data() QTest::newRow( "Tarawa E" ) << 0.0 << 179.0 << QString( "Tarawa" ); QTest::newRow( "Tarawa W" ) << 0.0 << -179.0 << QString( "Tarawa" ); + QTest::newRow( "Johannesburg" ) << -26.0 << 28.0 << QString( "Johannesburg" ); // South Africa + QTest::newRow( "Maseru" ) << -29.0 << 27.0 << QString( "Maseru" ); // Lesotho + QTest::newRow( "Windhoek" ) << -22.0 << 17.0 << QString( "Windhoek" ); // Namibia + QTest::newRow( "Port Elisabeth" ) << -33.0 << 25.0 << QString( "Johannesburg" ); // South Africa + QTest::newRow( "Cape Town" ) << -33.0 << 18.0 << QString( "Johannesburg" ); // South Africa } void From e35992cf0bc346da2f833d1a35c7326f00c2b1a2 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 00:05:40 +1000 Subject: [PATCH 76/91] [libcalamares] Add spot-patches to timezone data - for the purposes of Calamares's nearest-location selection algorithm for timezone selection, introduce spot patches: alternate markers on the map to indicate "things close to here belong in this timezone". - hide the implementation detail in the find() methods. --- src/libcalamares/locale/TimeZone.cpp | 64 +++++++++++++++++++++------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 4cd2bfb9a..ddc705548 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -183,17 +183,30 @@ loadTZData( RegionVector& regions, ZoneVector& zones, QTextStream& in ) } } -static void -loadTZData( RegionVector& regions, ZoneVector& zones ) -{ - QFile file( TZ_DATA_FILE ); - if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) - { - QTextStream in( &file ); - loadTZData( regions, zones, in ); - } -} - +/** @brief Extra, fake, timezones + * + * The timezone locations in zone.tab are not always very useful, + * given Calamares's standard "nearest zone" algorithm: for instance, + * in most locations physically in the country of South Africa, + * Maseru (the capital of Lesotho, and location for timezone Africa/Maseru) + * is closer than Johannesburg (the location for timezone Africa/Johannesburg). + * + * The algorithm picks the wrong place. This is for instance annoying + * when clicking on Cape Town, you get Maseru, and to get Johannesburg + * you need to click somewhere very carefully north of Maserru. + * + * These alternate zones are used to introduce "extra locations" + * into the timezone database, in order to influence the closest-location + * algorithm. Lines are formatted just like in zone.tab: remember the \n + */ +static const char altZones[] = + /* This extra zone is north-east of Karoo National park, + * and means that Western Cape province and a good chunk of + * Northern- and Eastern- Cape provinces get pulled in to Johannesburg. + * Bloemfontein is still closer to Maseru than either correct zone, + * but this is a definite improvement. + */ + "ZA -3230+02259 Africa/Johannesburg\n"; class Private : public QObject { @@ -201,13 +214,27 @@ class Private : public QObject public: RegionVector m_regions; ZoneVector m_zones; + ZoneVector m_altZones; //< Extra locations for zones Private() { m_regions.reserve( 12 ); // reasonable guess m_zones.reserve( 452 ); // wc -l /usr/share/zoneinfo/zone.tab - loadTZData( m_regions, m_zones ); + // Load the official timezones + { + QFile file( TZ_DATA_FILE ); + if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + QTextStream in( &file ); + loadTZData( m_regions, m_zones, in ); + } + } + // Load the alternate zones (see documentation at altZones) + { + QTextStream in( altZones ); + loadTZData( m_regions, m_altZones, in ); + } std::sort( m_regions.begin(), m_regions.end(), []( const RegionData* lhs, const RegionData* rhs ) { return lhs->key() < rhs->key(); @@ -343,9 +370,9 @@ ZonesModel::find( const QString& region, const QString& zone ) const } STATICTEST const TimeZoneData* -find( const ZoneVector& zones, const std::function< double( const TimeZoneData* ) >& distanceFunc ) +find( double startingDistance, const ZoneVector& zones, const std::function< double( const TimeZoneData* ) >& distanceFunc ) { - double smallestDistance = 1000000.0; + double smallestDistance = startingDistance; const TimeZoneData* closest = nullptr; for ( const auto* zone : zones ) @@ -363,7 +390,14 @@ find( const ZoneVector& zones, const std::function< double( const TimeZoneData* const TimeZoneData* ZonesModel::find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const { - return CalamaresUtils::Locale::find( m_private->m_zones, distanceFunc ); + const auto* officialZone = CalamaresUtils::Locale::find( 1000000.0, m_private->m_zones, distanceFunc ); + const auto* altZone = CalamaresUtils::Locale::find( distanceFunc( officialZone ), m_private->m_altZones, distanceFunc ); + + // If nothing was closer than the official zone already was, altZone is + // nullptr; but if there is a spot-patch, then we need to re-find + // the zone by name, since we want to always return pointers into + // m_zones, not into the alternative spots. + return altZone ? find( altZone->region(), altZone->zone() ) : officialZone; } const TimeZoneData* From 5e5701363c0bea958e20497d736e144c17685780 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 00:20:04 +1000 Subject: [PATCH 77/91] [libcalamares] Test the spot-patch for Johannesburg - Add a note about notation, degrees-minutes --- src/libcalamares/locale/Tests.cpp | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index d9bfb9133..b755a61a5 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -54,6 +54,7 @@ private Q_SLOTS: void testTZIterator(); void testLocationLookup_data(); void testLocationLookup(); + void testLocationLookup2(); }; LocaleTests::LocaleTests() {} @@ -423,6 +424,37 @@ LocaleTests::testLocationLookup() QCOMPARE( zone->zone(), name ); } +void +LocaleTests::testLocationLookup2() +{ + // Official + // ZA -2615+02800 Africa/Johannesburg + // Spot patch + // "ZA -3230+02259 Africa/Johannesburg\n"; + + const CalamaresUtils::Locale::ZonesModel zones; + const auto* zone = zones.find( -26.15, 28.00 ); + QCOMPARE( zone->zone(), QString( "Johannesburg" ) ); + // The TZ data sources use minutes-and-seconds notation, + // so "2615" is 26 degrees, 15 minutes, and 15 minutes is + // one-quarter of a degree. + QCOMPARE( zone->latitude(), -26.25 ); + QCOMPARE( zone->longitude(), 28.00 ); + + // Elsewhere in South Africa + const auto* altzone = zones.find( -32.0, 22.0 ); + QCOMPARE( altzone, zone ); // same pointer + QCOMPARE( altzone->zone(), QString( "Johannesburg" ) ); + QCOMPARE( altzone->latitude(), -26.25 ); + QCOMPARE( altzone->longitude(), 28.00 ); + + altzone = zones.find( -29.0, 27.0 ); + QCOMPARE( altzone->zone(), QString( "Maseru" ) ); + // -2928, that's -29 and 28/60 of a degree, is almost half, but we don't want + // to fall foul of variations in double-precision + QCOMPARE( trunc( altzone->latitude() * 1000.0 ), -29466 ); +} + QTEST_GUILESS_MAIN( LocaleTests ) From fe3495ff97d21b3f8e39d71e6fe64ec24a9b355f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 22:54:59 +0200 Subject: [PATCH 78/91] [libcalamares] Expand KPMCore tests - check on FS names as well --- src/libcalamares/partition/KPMTests.cpp | 34 +++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/libcalamares/partition/KPMTests.cpp b/src/libcalamares/partition/KPMTests.cpp index 0bdb29a15..ed34c2701 100644 --- a/src/libcalamares/partition/KPMTests.cpp +++ b/src/libcalamares/partition/KPMTests.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,14 +16,12 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ #include "utils/Logger.h" #include +#include #include @@ -36,6 +35,7 @@ private Q_SLOTS: void initTestCase(); void testFlagNames(); + void testFSNames(); }; KPMTests::KPMTests() {} @@ -51,12 +51,13 @@ KPMTests::initTestCase() void KPMTests::testFlagNames() { + cDebug() << "Partition flags according to KPMCore:"; int f = 1; QStringList names; QString s; while ( !( s = PartitionTable::flagName( static_cast< PartitionTable::Flag >( f ) ) ).isEmpty() ) { - cDebug() << f << s; + cDebug() << Logger::SubEntry << f << s; names.append( s ); f <<= 1; @@ -78,6 +79,29 @@ KPMTests::testFlagNames() #endif } +void +KPMTests::testFSNames() +{ + cDebug() << "FileSystem names according to KPMCore:"; + + // This uses KPMCore directly, rather than Calamares partition/FileSystem.h + // which doesn't wrap nameForType() -- it just provides more meaningful + // names for FS naming on FileSystem objects. + QStringList fsNames; + const auto fstypes = FileSystem::types(); + fsNames.reserve( fstypes.count() ); + for ( const auto t : fstypes ) + { + QString s = FileSystem::nameForType( t, { "C" } ); // Untranslated + cDebug() << Logger::SubEntry << s; + fsNames.append( s ); + } + + QVERIFY( fsNames.contains( "ext2" ) ); + QVERIFY( fsNames.contains( "ext4" ) ); + QVERIFY( fsNames.contains( "reiser" ) ); +} + QTEST_GUILESS_MAIN( KPMTests ) From 537aad1222a1676163848e2b6ed1d9af2c383c1a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sat, 8 Aug 2020 23:32:09 +0200 Subject: [PATCH 79/91] [libcalamares] SPDX, DLLEXPORT on partition/FileSystem.h --- src/libcalamares/partition/FileSystem.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcalamares/partition/FileSystem.h b/src/libcalamares/partition/FileSystem.h index a683aab47..6f058fd81 100644 --- a/src/libcalamares/partition/FileSystem.h +++ b/src/libcalamares/partition/FileSystem.h @@ -1,8 +1,9 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2014 Aurélien Gâteau * SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +18,6 @@ * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * */ /* @@ -30,13 +28,15 @@ #ifndef PARTITION_FILESYSTEM_H #define PARTITION_FILESYSTEM_H +#include "DllMacro.h" + #include namespace CalamaresUtils { namespace Partition { -QString prettyNameForFileSystemType( FileSystem::Type t ); +QString DLLEXPORT prettyNameForFileSystemType( FileSystem::Type t ); static inline QString untranslatedFS( FileSystem& fs ) From 62a8ee9708ab1da451681187d2edeca34671e5ab Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 00:00:14 +0200 Subject: [PATCH 80/91] [libcalamares] Add name-for-partition-type method - add apidox to all the untranslatedFS() methods - add the most-basic of untranslatedFS(), which works on a given FileSystem::Type; this one can handle special cases where Cala needs a different untranslated name than what KPMCore provides. --- src/libcalamares/partition/FileSystem.cpp | 14 +++++++++++- src/libcalamares/partition/FileSystem.h | 20 ++++++++++++++++- src/libcalamares/partition/KPMTests.cpp | 26 +++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/partition/FileSystem.cpp b/src/libcalamares/partition/FileSystem.cpp index 965a1a8af..6fda6b41a 100644 --- a/src/libcalamares/partition/FileSystem.cpp +++ b/src/libcalamares/partition/FileSystem.cpp @@ -1,5 +1,5 @@ /* === This file is part of Calamares - === - * + * * SPDX-FileCopyrightText: 2014 Aurélien Gâteau * SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot @@ -74,5 +74,17 @@ prettyNameForFileSystemType( FileSystem::Type t ) } } +QString +untranslatedFS( FileSystem::Type t ) +{ + switch ( t ) + { + case FileSystem::Type::ReiserFS: + return QStringLiteral( "reiserfs" ); + default: + return FileSystem::nameForType( t, { QStringLiteral( "C" ) } ); + } +} + } // namespace Partition } // namespace CalamaresUtils diff --git a/src/libcalamares/partition/FileSystem.h b/src/libcalamares/partition/FileSystem.h index 6f058fd81..03f6ff3bc 100644 --- a/src/libcalamares/partition/FileSystem.h +++ b/src/libcalamares/partition/FileSystem.h @@ -38,12 +38,30 @@ namespace Partition { QString DLLEXPORT prettyNameForFileSystemType( FileSystem::Type t ); +/** @brief Returns a machine-readable identifier for the filesystem type + * + * This identifier is used in filesystem manipulation -- + * e.g. when mounting the filesystem, or in /etc/fstab. It + * is almost always just what KPMCore says it is, with + * the following exceptions: + * - reiserfs is called "reiser" by KPMCore, "reiserfs" by Calamares + */ +QString DLLEXPORT untranslatedFS( FileSystem::Type t ); + +/** @brief Returns the machine-readable identifier for the given @p fs + * + * See notes for untranslatedFS(), above. + */ static inline QString untranslatedFS( FileSystem& fs ) { - return fs.name( { QStringLiteral( "C" ) } ); + return untranslatedFS( fs.type() ); } +/** @brief Returns a machine-readable identifier for the given @p fs + * + * Returns an empty string is the @p fs is not valid (e.g. nullptr). + */ static inline QString untranslatedFS( FileSystem* fs ) { diff --git a/src/libcalamares/partition/KPMTests.cpp b/src/libcalamares/partition/KPMTests.cpp index ed34c2701..d702c8a01 100644 --- a/src/libcalamares/partition/KPMTests.cpp +++ b/src/libcalamares/partition/KPMTests.cpp @@ -20,6 +20,8 @@ #include "utils/Logger.h" +#include "FileSystem.h" + #include #include @@ -100,6 +102,30 @@ KPMTests::testFSNames() QVERIFY( fsNames.contains( "ext2" ) ); QVERIFY( fsNames.contains( "ext4" ) ); QVERIFY( fsNames.contains( "reiser" ) ); + + QStringList calaFSNames; + calaFSNames.reserve( fstypes.count() ); + for ( const auto t : fstypes ) + { + QString s = CalamaresUtils::Partition::untranslatedFS( t ); + calaFSNames.append( s ); + } + + QVERIFY( calaFSNames.contains( "ext2" ) ); + QVERIFY( calaFSNames.contains( "ext4" ) ); + QVERIFY( !calaFSNames.contains( "reiser" ) ); + QVERIFY( calaFSNames.contains( "reiserfs" ) ); // whole point of Cala's own implementation + + // Lists are the same except for .. the exceptions + QStringList exceptionalNames { "reiser", "reiserfs" }; + for ( const auto& s : fsNames ) + { + QVERIFY( exceptionalNames.contains( s ) || calaFSNames.contains( s ) ); + } + for ( const auto& s : calaFSNames ) + { + QVERIFY( exceptionalNames.contains( s ) || fsNames.contains( s ) ); + } } From 4808201944173c8a4002b2dcb3d71bed8687562d Mon Sep 17 00:00:00 2001 From: Calamares CI Date: Sun, 9 Aug 2020 20:49:43 +0200 Subject: [PATCH 81/91] i18n: [calamares] Automatic merge of Transifex translations --- lang/calamares_as.ts | 166 ++++++++++++++++++++++------------------ lang/calamares_az.ts | 17 ++-- lang/calamares_az_AZ.ts | 11 +-- lang/calamares_ja.ts | 11 +-- 4 files changed, 113 insertions(+), 92 deletions(-) diff --git a/lang/calamares_as.ts b/lang/calamares_as.ts index b087c0ba3..9f1da1ad3 100644 --- a/lang/calamares_as.ts +++ b/lang/calamares_as.ts @@ -212,17 +212,17 @@ Loading ... - + ভৰ্টিকৰন ... QML Step <i>%1</i>. - + QML Step <i>%1</i>. Loading failed. - + ভৰ্টিকৰন বিফল | @@ -717,7 +717,7 @@ The installer will quit and all changes will be lost. Set timezone to %1/%2. - + সময় অঞ্চলৰ সিদ্ধান্ত কৰক %`1%2 @@ -742,7 +742,7 @@ The installer will quit and all changes will be lost. Network Installation. (Disabled: internal error) - + নেটৱৰ্ক ইনস্তলেচন। (নিস্ক্ৰিয়: ভিতৰুৱা দোষ) @@ -777,22 +777,22 @@ The installer will quit and all changes will be lost. <h1>Welcome to the Calamares setup program for %1</h1> - + %1ৰ Calamares চেত্ আপ প্ৰগ্ৰামলৈ আদৰণি জনাইছো। <h1>Welcome to %1 setup</h1> - + <h1> %1 চেত্ আপলৈ আদৰণি জনাইছো।</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>%1ৰ কেলামাৰেচ ইনস্তলাৰলৈ আদৰণি জনাইছো।</h1> <h1>Welcome to the %1 installer</h1> - + <h1>%1 ইনস্তলাৰলৈ আদৰণি জনাইছো।</h1> @@ -802,7 +802,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as username. - + '%1'ক ব্যৱহাৰকাৰীৰ নাম হিচাপে ব্যৱহাৰ কৰা অবধ্য | @@ -827,7 +827,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as hostname. - + '%1'ক আয়োজকৰ নাম হিচাপে ব্যৱহাৰ কৰা অবধ্য | @@ -1768,7 +1768,7 @@ The installer will quit and all changes will be lost. Could not configure LUKS key file on partition %1. - + %1 বিভাজনত LUKS কি ফাইল কনফিগাৰ কৰিব পৰা নগ'ল। @@ -1796,7 +1796,9 @@ The installer will quit and all changes will be lost. Please select your preferred location on the map so the installer can suggest the locale and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming. - + অনুগ্ৰহ কৰি নিজৰ প্রিয় স্থান নক্সাখ্নত বাছক জাতে ইস্নস্তালাৰটোৱে অপোনাক থলী + আৰু সময় অঞ্চলৰ ছেটিংছ আপোৰ বাবে মিলাই দায়ক | আপোনি পৰামৰ্শমূলক ছেটিংছবোৰক তলত অনুকূলিত কৰিব পাৰে | নক্সাখ্নত পৈন্তেৰদালক টানি অনুসন্ধান কৰিব | + আৰু +/- বুটামেৰে যোম in/out কৰক বা মাউছৰ সচৰোলৰও ব্যবহাৰ কৰিব পাৰে | @@ -1810,92 +1812,92 @@ The installer will quit and all changes will be lost. Office software - + কাৰ্যালয়ৰ ছফটৱেৰ Office package - + কাৰ্যালয়ৰ পেকেজ Browser software - + ব্ৰাউজাৰৰ ছফটৱেৰ Browser package - + ব্ৰাউজাৰৰ পেকেজ Web browser - + ৱেব ব্ৰাউজাৰ Kernel - + কাৰ্ণেল Services - + সেৰ্ৱিচেস Login - + পৰীক্ষণ কৰক Desktop - + দেস্কেতোপ Applications - + এপ্লীকেছ্নচ Communication - + যোগাযোগ Development - + প্রবৃদ্ধি Office - + কাৰ্যালয় Multimedia - + মাল্টিমিডিয়া Internet - + ইণ্টাৰনেট Theming - + থিমীং Gaming - + খেলা Utilities - + সঁজুলি @@ -1903,7 +1905,7 @@ The installer will quit and all changes will be lost. Notes - + টোকা @@ -1942,12 +1944,12 @@ The installer will quit and all changes will be lost. Timezone: %1 - + সময় অঞ্চল: %1 To be able to select a timezone, make sure you are connected to the internet. Restart the installer after connecting. You can fine-tune Language and Locale settings below. - + সময় অঞ্চল বাছিবলৈ, ইণ্টাৰনেটত সংশ্লিষ্ট কৰি ৰাখিব | সংশ্লিষ্ট কৰি ইন্স্তালাৰটো পুনৰাৰম্ভ কৰক | আপোনি ভাসা অৰু থলীৰ ছেটিংছবোৰ তলত অনুকূলিত কৰিব পাৰে | @@ -2599,12 +2601,12 @@ The installer will quit and all changes will be lost. An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a FAT32 filesystem with the <strong>%3</strong> flag enabled and mount point <strong>%2</strong>.<br/><br/>You can continue without setting up an EFI system partition but your system may fail to start. - + %1 আৰম্ভ কৰিবলৈ এটা EFI চিছটেম থকাটো আৱশ্যক। <br/><br/>এটা EFI চিছটেম কন্ফিগাৰ কৰিবলৈ উভতি যাওক আৰু FAT32 ফাইল চিছটেম এটা বাচনি কৰক যিটোত <strong>%3</strong> ফ্লেগ সক্ষম হৈ আছে আৰু <strong>%2</strong> মাউন্ট্ পইণ্ট্ আছে।<br/><br/>আপুনি EFI চিছটেমবিভাজন কন্ফিগাৰ নকৰাকৈ অগ্ৰসৰ হ'ব পাৰে কিন্তু ইয়াৰ ফলত চিছটেম বিফল হ'ব পাৰে। An EFI system partition is necessary to start %1.<br/><br/>A partition was configured with mount point <strong>%2</strong> but its <strong>%3</strong> flag is not set.<br/>To set the flag, go back and edit the partition.<br/><br/>You can continue without setting the flag but your system may fail to start. - + %1 আৰম্ভ কৰিবলৈ এটা EFI চিছটেম থকাটো আৱশ্যক। %2 মাউন্ট্ পইন্ট্ নোহোৱকৈ কন্ফিগাৰ কৰা হৈছিল, কিন্তু ইয়াৰ esp ফ্লেগ ছেট কৰা হোৱা নাই। ফ্লেগ্ ছেট কৰিবলৈ উভতি যাওক আৰু বিভাজন সলনি কৰক। আপুনি ফ্লেগ ছেট নকৰাকৈ অগ্ৰসৰ হ'ব পাৰে কিন্তু ইয়াৰ ফলত চিছটেম বিফল হ'ব পাৰে। @@ -2614,12 +2616,12 @@ The installer will quit and all changes will be lost. Option to use GPT on BIOS - + GPTৰ BIOSত ব্যৱহাৰৰ বাবে বিকল্প A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.<br/><br/>To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the <strong>bios_grub</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. - + এটা GPT পৰ্তিসোন টেবুল সকলো স্যস্তেমৰ বাবে উত্তম বিকল্প হয় | এই ইন্সালাৰতোৱে তেনে স্থাপনকৰণ BIOS স্যস্তেমতো কৰে |<br/><br/>এটা GPT পৰ্তিসোন টেবুল কনফিগাৰ কৰিবলৈ ( যদি আগতে কৰা নাই ) পাছলৈ গৈ পৰ্তিসোন টেবুলখনক GPTলৈ পৰিৱৰ্তন কৰক, তাৰপাচত 8 MBৰ উনফোৰমেতেট পৰ্তিতিওন এটা বনাব | <strong>bios_grub</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. @@ -2639,7 +2641,7 @@ The installer will quit and all changes will be lost. There are no partitions to install on. - + ইনস্তল কৰিবলৈ কোনো বিভাজন নাই। @@ -2852,7 +2854,7 @@ Output: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + %1 চেত্ আপৰ বাবে পৰামৰ্শ দিয়া আৱশ্যকতা এই কম্পিউটাৰটোৱে পূৰ্ণ নকৰে। <br/>স্থাপন প্ৰক্ৰিয়া অবিৰত ৰাখিব পাৰিব, কিন্তু কিছুমান সুবিধা নিষ্ক্রিয় হৈ থাকিব। @@ -2963,13 +2965,14 @@ Output: <p>This computer does not satisfy the minimum requirements for installing %1.<br/> Installation cannot continue.</p> - + %1 ইনস্তলচেন​ৰ বাবে নিম্নতম আৱশ্যকতা এই কম্পিউটাৰটোৱে পূৰ্ণ নকৰে। + <br/>ইনস্তলচেন​ প্ৰক্ৰিয়া অবিৰত ৰাখিব নোৱাৰিব। <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + %1 চেত্ আপৰ বাবে পৰামৰ্শ দিয়া আৱশ্যকতা এই কম্পিউটাৰটোৱে পূৰ্ণ নকৰে। <br/>স্থাপন প্ৰক্ৰিয়া অবিৰত ৰাখিব পাৰিব, কিন্তু কিছুমান সুবিধা নিষ্ক্রিয় হৈ থাকিব। @@ -3435,28 +3438,28 @@ Output: KDE user feedback - + KDE ব্যৱহাৰকৰ্তাৰ সম্বন্ধীয় প্ৰতিক্ৰীয়া Configuring KDE user feedback. - + কনফিগাৰত KDE ব্যৱহাৰকৰ্তাৰ সম্বন্ধীয় প্ৰতিক্ৰীয়া Error in KDE user feedback configuration. - + KDE ব্যৱহাৰকৰ্তাৰ ফিডবেক কনফিগাৰেচনৰ ক্ৰুটি। Could not configure KDE user feedback correctly, script error %1. - + KDE ব্যৱহাৰকৰ্তাৰ প্ৰতিক্ৰিয়া ঠাকভাৱে কন্ফিগাৰ কৰিব পৰা নগ'ল, লিপি ক্ৰুটি %1। Could not configure KDE user feedback correctly, Calamares error %1. - + KDE ব্যৱহাৰকৰ্তাৰ প্ৰতিক্ৰিয়া ঠাকভাৱে কন্ফিগাৰ কৰিব পৰা নগ'ল, কেলামাৰেচ ক্ৰুটি %1। @@ -3503,7 +3506,7 @@ Output: <html><head/><body><p>Click here to send <span style=" font-weight:600;">no information at all</span> about your installation.</p></body></html> - + <html><head/><body><p>এইটো বাচনি কৰি, ইনস্তলচেন​ৰ বিষয়ে <span style=" font-weight:600;">মুঠতে একো তথ্য</span> আপুনি নপঠায়।</p></body></html> @@ -3518,17 +3521,17 @@ Output: By selecting this you will send information about your installation and hardware. This information will only be sent <b>once</b> after the installation finishes. - + এইটো বাচনি কৰি আপুনি ইনস্তলচেন​ আৰু হাৰ্ডৱেৰৰ বিষয়ে তথ্য পঠাব। ইনস্তলচেন​ৰ পিছত <b>এই তথ্য এবাৰ পঠোৱা হ'ব</b>। By selecting this you will periodically send information about your <b>machine</b> installation, hardware and applications, to %1. - + এইটো বাচনি কৰি আপুনি ইনস্তলচেন​, হাৰ্ডৱেৰ আৰু এপ্লিকেচনৰ বিষয়ে <b>মেচিন</b> %1লৈ তথ্য পঠাব। By selecting this you will regularly send information about your <b>user</b> installation, hardware, applications and application usage patterns, to %1. - + এইটো বাচনি কৰি আপুনি ইনস্তলচেন​, হাৰ্ডৱেৰ আৰু এপ্লিকেচনৰ বিষয়ে <b>ব্যৱহাৰকৰ্তা</b> %1লৈ তথ্য পঠাব। @@ -3727,7 +3730,7 @@ Output: <h1>%1</h1><br/><strong>%2<br/>for %3</strong><br/><br/>Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>Thanks to <a href="https://calamares.io/team/">the Calamares team</a> and the <a href="https://www.transifex.com/calamares/calamares/">Calamares translators team</a>.<br/><br/><a href="https://calamares.io/">Calamares</a> development is sponsored by <br/><a href="http://www.blue-systems.com/">Blue Systems</a> - Liberating Software. - + <h1>%1</h1><br/><strong>%2<br/>ৰ বাবে %3</strong><br/><br/> মালিকিস্বত্ত 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>মালিকিস্বত্ত 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/><a href="https://calamares.io/team/">Calamares দল</a> আৰু <a href="https://www.transifex.com/calamares/calamares/">কেলামাৰেচ অনুবাদক দল</a>ক ধন্যবাদ জনাইছো।<br/><br/><a href="https://calamares.io/">Calamares</a>ৰ বিকাশ<br/><a href="http://www.blue-systems.com/">Blue Systems</a>- Liberating Softwareৰ দ্বাৰা প্ৰযোজিত। @@ -3762,12 +3765,23 @@ Output: development is sponsored by <br/> <a href='http://www.blue-systems.com/'>Blue Systems</a> - Liberating Software. - + <h1>%1</h1><br/> + <strong>%2<br/> + ৰ বাবে %3</strong><br/><br/> + মালিকিস্বত্ত 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/> + মালিকিস্বত্ত 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/> + ক ধন্যবাদ জনাইছো<a href='https://calamares.io/team/'> Calamares দল + আৰু <a href='https://www.transifex.com/calamares/calamares/'>Calamares + অনুবাদক দল</a>.<br/><br/> + <a href='https://calamares.io/'>Calamares</a> + ৰ বিকাশ<br/> + <a href='http://www.blue-systems.com/'>Blue Systems</a> - + Liberating Softwareৰ দ্বাৰা প্ৰযোজিত।. Back - + পাছলৈ @@ -3776,18 +3790,20 @@ Output: <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>. - + <h1>ভাষা</h1> </br> + চিছটেমৰ স্থানীয় ছেটিংস্ কমাণ্ডলাইনৰ কিছুমান উপভোক্তা ইন্টাৰফেছ উপাদানৰ ভাষা আৰু আখৰবোৰত প্ৰভাৱ পেলায়। বৰ্তমান ছেটিংস্ হ'ল: <strong>%1</strong>. <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>স্থানীয়</h1> </br> + চিছটেমৰ স্থানীয় ছেটিংসে উপাদানৰ নম্বৰ আৰু তাৰিখ সজ্জা প্ৰভাৱ পেলায়। বৰ্তমান ছেটিংস্ হ'ল: <strong>%1</strong>. Back - + পাছলৈ @@ -3795,44 +3811,44 @@ Output: Keyboard Model - + কিবোৰ্ড মডেল Pick your preferred keyboard model or use the default one based on the detected hardware - + আপোনাৰ প্ৰিয় কিবোৰ্ড মডেল চয়ন কৰক বা আপোনাৰ হাৰ্ডৱেৰৰ গতানুগতিকখ্নক ব্যৱহাৰ কৰক | Refresh - + সতেজ কৰক Layouts - + লেআউট Keyboard Layout - + কিবোৰ্ড লেআউট Models - + মডেল Variants - + ভিন্ন ৰুপ Test your keyboard - + কিবোৰ্ড পৰীক্ষা কৰক @@ -3840,7 +3856,7 @@ Output: Change - + সলনি @@ -3849,7 +3865,8 @@ Output: <h3>%1</h3> <p>These are example release notes.</p> - + <h3>%1</h3> + <p>এই খিনি ৰিলিজ নোতৰ উদাহৰণ </p> @@ -3882,7 +3899,7 @@ Output: Back - + পাছলৈ @@ -3891,32 +3908,33 @@ Output: <h3>Welcome to the %1 <quote>%2</quote> installer</h3> <p>This program will ask you some questions and set up %1 on your computer.</p> - + <h3>স্বাগতম আপোনাক %1 <quote>%2</quote> ইন্সালাৰটোত</h3> + <p>এই প্ৰোগ্ৰেমটোএয়ে অপোনাক কিৱছোমান প্ৰশ্ন সুধিব আৰু আপোনাৰ কোম্পিউটাৰত %1 স্থাপনকৰণ কৰিব |</p> About - + সম্পর্কে Support - + সহায় Known issues - + জ্ঞাত সমস্যা Release notes - + মুক্তি টোকা Donate - + দান কৰক diff --git a/lang/calamares_az.ts b/lang/calamares_az.ts index 4656531e5..c762de2df 100644 --- a/lang/calamares_az.ts +++ b/lang/calamares_az.ts @@ -6,7 +6,7 @@ The <strong>boot environment</strong> of this system.<br><br>Older x86 systems only support <strong>BIOS</strong>.<br>Modern systems usually use <strong>EFI</strong>, but may also show up as BIOS if started in compatibility mode. - Bu sistemin <strong>açılış mühiti</strong>.<br><br>Köhnə x86 sistemlər yalnız <strong>BIOS</strong> dəstəkləyir.<br>Müasir sistemlər isə adətən <strong>EFI</strong> istifadə edir, lakin açılış mühiti əgər uyğun rejimdə başladılmışsa, həmçinin BİOS istiafadə edə bilər. + Sistemin <strong>açılış mühiti</strong>.<br><br>Köhnə x86 sistemlər yalnız <strong>BIOS</strong> dəstəkləyir.<br>Müasir sistemlər isə adətən <strong>EFI</strong> istifadə edir, lakin açılış mühiti əgər uyğun rejimdə başladılmışsa, həmçinin BİOS istiafadə edə bilər. @@ -65,12 +65,12 @@ GlobalStorage - Ümumi yaddaş + ÜmumiYaddaş JobQueue - Tapşırıq sırası + TapşırıqSırası @@ -530,7 +530,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + <strong>Əl ilə bölmək</strong><br/>Siz bölməni özünüz yarada və ölçüsünü dəyişə bilərsiniz. @@ -717,7 +717,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. Set timezone to %1/%2. - + Saat quraşağını təyin etmək %1/%2 @@ -802,7 +802,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as username. - + İstifadəçi adı '%1' ola bilməz @@ -827,7 +827,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as hostname. - + Host_adı '%1' ola bilməz @@ -3800,7 +3800,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Yerlər</h1></br> + Sistemin məkan ayarları say və tarix formatlarəna təsir edir. Cari ayar <strong>%1</strong>-dir diff --git a/lang/calamares_az_AZ.ts b/lang/calamares_az_AZ.ts index 9bc6e2aab..0c1077022 100644 --- a/lang/calamares_az_AZ.ts +++ b/lang/calamares_az_AZ.ts @@ -530,7 +530,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + <strong>Əl ilə bölmək</strong><br/>Siz bölməni özünüz yarada və ölçüsünü dəyişə bilərsiniz. @@ -717,7 +717,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. Set timezone to %1/%2. - + Saat quraşağını təyin etmək %1/%2 @@ -802,7 +802,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as username. - + İstifadəçi adı '%1' ola bilməz @@ -827,7 +827,7 @@ Bu proqramdan çıxılacaq və bütün dəyişikliklər itiriləcəkdir. '%1' is not allowed as hostname. - + Host_adı '%1' ola bilməz @@ -3800,7 +3800,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>Yerlər</h1></br> + Sistemin məkan ayarları say və tarix formatlarəna təsir edir. Cari ayar <strong>%1</strong>-dir diff --git a/lang/calamares_ja.ts b/lang/calamares_ja.ts index fc20c54b3..8febc0aba 100644 --- a/lang/calamares_ja.ts +++ b/lang/calamares_ja.ts @@ -715,7 +715,7 @@ The installer will quit and all changes will be lost. Set timezone to %1/%2. - + タイムゾーンを %1/%2 に設定します。 @@ -725,7 +725,7 @@ The installer will quit and all changes will be lost. The numbers and dates locale will be set to %1. - 数字と日付のロケールを %1 に設定します。 + 数値と日付のロケールを %1 に設定します。 @@ -800,7 +800,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as username. - + '%1' はユーザー名として許可されていません。 @@ -825,7 +825,7 @@ The installer will quit and all changes will be lost. '%1' is not allowed as hostname. - + '%1' はホスト名として許可されていません。 @@ -3799,7 +3799,8 @@ Output: <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + <h1>ロケール</h1> </br> + システムのロケール設定は、数値と日付の形式に影響を及ぼします。現在の設定は <strong>%1</strong> です。 From f08b4e502a00ffbae5ead40ae7b3d259be25c5fb Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 20:58:16 +0200 Subject: [PATCH 82/91] i18n: update English source translations --- lang/calamares_en.ts | 250 +++++++++--------- lang/python.pot | 155 +++++------ .../dummypythonqt/lang/dummypythonqt.pot | 2 +- 3 files changed, 199 insertions(+), 208 deletions(-) diff --git a/lang/calamares_en.ts b/lang/calamares_en.ts index e6e44b274..e4773535e 100644 --- a/lang/calamares_en.ts +++ b/lang/calamares_en.ts @@ -510,134 +510,134 @@ The installer will quit and all changes will be lost. Form - + Select storage de&vice: Select storage de&vice: - - - - + + + + Current: Current: - + After: After: - + <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + Reuse %1 as home partition for %2. Reuse %1 as home partition for %2. - + <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. - + Boot loader location: Boot loader location: - + <strong>Select a partition to install on</strong> <strong>Select a partition to install on</strong> - + An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. - + The EFI system partition at %1 will be used for starting %2. The EFI system partition at %1 will be used for starting %2. - + EFI system partition: EFI system partition: - + This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - - - - + + + + <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - - - - + + + + <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. - - - - + + + + <strong>Replace a partition</strong><br/>Replaces a partition with %1. <strong>Replace a partition</strong><br/>Replaces a partition with %1. - + This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + No Swap No Swap - + Reuse Swap Reuse Swap - + Swap (no Hibernate) Swap (no Hibernate) - + Swap (with Hibernate) Swap (with Hibernate) - + Swap to file Swap to file @@ -715,17 +715,17 @@ The installer will quit and all changes will be lost. Set keyboard layout to %1/%2. - + Set timezone to %1/%2. Set timezone to %1/%2. - + The system language will be set to %1. The system language will be set to %1. - + The numbers and dates locale will be set to %1. The numbers and dates locale will be set to %1. @@ -795,42 +795,42 @@ The installer will quit and all changes will be lost. <h1>Welcome to the %1 installer</h1> - + Your username is too long. Your username is too long. - + '%1' is not allowed as username. '%1' is not allowed as username. - + Your username must start with a lowercase letter or underscore. Your username must start with a lowercase letter or underscore. - + Only lowercase letters, numbers, underscore and hyphen are allowed. Only lowercase letters, numbers, underscore and hyphen are allowed. - + Your hostname is too short. Your hostname is too short. - + Your hostname is too long. Your hostname is too long. - + '%1' is not allowed as hostname. '%1' is not allowed as hostname. - + Only letters, numbers, underscore and hyphen are allowed. Only letters, numbers, underscore and hyphen are allowed. @@ -1251,7 +1251,8 @@ The installer will quit and all changes will be lost. Confirm passphrase - + + Please enter the same passphrase in both boxes. Please enter the same passphrase in both boxes. @@ -1703,18 +1704,18 @@ The installer will quit and all changes will be lost. LocalePage - + Region: Region: - + Zone: Zone: - - + + &Change... &Change... @@ -1792,7 +1793,12 @@ The installer will quit and all changes will be lost. Map - + + Timezone: %1 + Timezone: %1 + + + Please select your preferred location on the map so the installer can suggest the locale and timezone settings for you. You can fine-tune the suggested settings below. Search the map by dragging to move and using the +/- buttons to zoom in/out or use mouse scrolling for zooming. @@ -1955,247 +1961,247 @@ The installer will quit and all changes will be lost. PWQ - + Password is too short Password is too short - + Password is too long Password is too long - + Password is too weak Password is too weak - + Memory allocation error when setting '%1' Memory allocation error when setting '%1' - + Memory allocation error Memory allocation error - + The password is the same as the old one The password is the same as the old one - + The password is a palindrome The password is a palindrome - + The password differs with case changes only The password differs with case changes only - + The password is too similar to the old one The password is too similar to the old one - + The password contains the user name in some form The password contains the user name in some form - + The password contains words from the real name of the user in some form The password contains words from the real name of the user in some form - + The password contains forbidden words in some form The password contains forbidden words in some form - + The password contains less than %1 digits The password contains less than %1 digits - + The password contains too few digits The password contains too few digits - + The password contains less than %1 uppercase letters The password contains less than %1 uppercase letters - + The password contains too few uppercase letters The password contains too few uppercase letters - + The password contains less than %1 lowercase letters The password contains less than %1 lowercase letters - + The password contains too few lowercase letters The password contains too few lowercase letters - + The password contains less than %1 non-alphanumeric characters The password contains less than %1 non-alphanumeric characters - + The password contains too few non-alphanumeric characters The password contains too few non-alphanumeric characters - + The password is shorter than %1 characters The password is shorter than %1 characters - + The password is too short The password is too short - + The password is just rotated old one The password is just rotated old one - + The password contains less than %1 character classes The password contains less than %1 character classes - + The password does not contain enough character classes The password does not contain enough character classes - + The password contains more than %1 same characters consecutively The password contains more than %1 same characters consecutively - + The password contains too many same characters consecutively The password contains too many same characters consecutively - + The password contains more than %1 characters of the same class consecutively The password contains more than %1 characters of the same class consecutively - + The password contains too many characters of the same class consecutively The password contains too many characters of the same class consecutively - + The password contains monotonic sequence longer than %1 characters The password contains monotonic sequence longer than %1 characters - + The password contains too long of a monotonic character sequence The password contains too long of a monotonic character sequence - + No password supplied No password supplied - + Cannot obtain random numbers from the RNG device Cannot obtain random numbers from the RNG device - + Password generation failed - required entropy too low for settings Password generation failed - required entropy too low for settings - + The password fails the dictionary check - %1 The password fails the dictionary check - %1 - + The password fails the dictionary check The password fails the dictionary check - + Unknown setting - %1 Unknown setting - %1 - + Unknown setting Unknown setting - + Bad integer value of setting - %1 Bad integer value of setting - %1 - + Bad integer value Bad integer value - + Setting %1 is not of integer type Setting %1 is not of integer type - + Setting is not of integer type Setting is not of integer type - + Setting %1 is not of string type Setting %1 is not of string type - + Setting is not of string type Setting is not of string type - + Opening the configuration file failed Opening the configuration file failed - + The configuration file is malformed The configuration file is malformed - + Fatal failure Fatal failure - + Unknown error Unknown error - + Password is empty Password is empty @@ -2529,67 +2535,67 @@ The installer will quit and all changes will be lost. PartitionViewStep - + Gathering system information... Gathering system information... - + Partitions Partitions - + Install %1 <strong>alongside</strong> another operating system. Install %1 <strong>alongside</strong> another operating system. - + <strong>Erase</strong> disk and install %1. <strong>Erase</strong> disk and install %1. - + <strong>Replace</strong> a partition with %1. <strong>Replace</strong> a partition with %1. - + <strong>Manual</strong> partitioning. <strong>Manual</strong> partitioning. - + Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). - + <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. - + <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. - + <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). - + Disk <strong>%1</strong> (%2) Disk <strong>%1</strong> (%2) - + Current: Current: - + After: After: @@ -2634,12 +2640,12 @@ The installer will quit and all changes will be lost. A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.<br/><br/>There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.<br/>You may continue if you wish, but filesystem unlocking will happen later during system startup.<br/>To encrypt the boot partition, go back and recreate it, selecting <strong>Encrypt</strong> in the partition creation window. - + has at least one disk device available. has at least one disk device available. - + There are no partitions to install on. There are no partitions to install on. @@ -3547,17 +3553,17 @@ Output: UsersPage - + <small>If more than one person will use this computer, you can create multiple accounts after setup.</small> <small>If more than one person will use this computer, you can create multiple accounts after setup.</small> - + <small>If more than one person will use this computer, you can create multiple accounts after installation.</small> <small>If more than one person will use this computer, you can create multiple accounts after installation.</small> - + Your passwords do not match! Your passwords do not match! @@ -3565,7 +3571,7 @@ Output: UsersViewStep - + Users Users @@ -3856,7 +3862,7 @@ Output: localeq - + Change Change diff --git a/lang/python.pot b/lang/python.pot index 1c20ae7b5..7e3d85b67 100644 --- a/lang/python.pot +++ b/lang/python.pot @@ -2,29 +2,29 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-07-29 11:03+0200\n" +"POT-Creation-Date: 2020-08-09 20:56+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: \n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: src/modules/grubcfg/main.py:37 msgid "Configure GRUB." -msgstr "Configure GRUB." +msgstr "" #: src/modules/mount/main.py:38 msgid "Mounting partitions." -msgstr "Mounting partitions." +msgstr "" #: src/modules/mount/main.py:150 src/modules/initcpiocfg/main.py:205 #: src/modules/initcpiocfg/main.py:209 @@ -36,179 +36,172 @@ msgstr "Mounting partitions." #: src/modules/fstab/main.py:338 src/modules/localecfg/main.py:144 #: src/modules/networkcfg/main.py:48 msgid "Configuration Error" -msgstr "Configuration Error" +msgstr "" #: src/modules/mount/main.py:151 src/modules/initcpiocfg/main.py:206 #: src/modules/luksopenswaphookcfg/main.py:96 src/modules/rawfs/main.py:174 #: src/modules/initramfscfg/main.py:95 src/modules/openrcdmcryptcfg/main.py:79 #: src/modules/fstab/main.py:333 msgid "No partitions are defined for
{!s}
to use." -msgstr "No partitions are defined for
{!s}
to use." +msgstr "" #: src/modules/services-systemd/main.py:35 msgid "Configure systemd services" -msgstr "Configure systemd services" +msgstr "" #: src/modules/services-systemd/main.py:68 #: src/modules/services-openrc/main.py:102 msgid "Cannot modify service" -msgstr "Cannot modify service" +msgstr "" #: src/modules/services-systemd/main.py:69 msgid "" "systemctl {arg!s} call in chroot returned error code {num!s}." msgstr "" -"systemctl {arg!s} call in chroot returned error code {num!s}." #: src/modules/services-systemd/main.py:72 #: src/modules/services-systemd/main.py:76 msgid "Cannot enable systemd service {name!s}." -msgstr "Cannot enable systemd service {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:74 msgid "Cannot enable systemd target {name!s}." -msgstr "Cannot enable systemd target {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:78 msgid "Cannot disable systemd target {name!s}." -msgstr "Cannot disable systemd target {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:80 msgid "Cannot mask systemd unit {name!s}." -msgstr "Cannot mask systemd unit {name!s}." +msgstr "" #: src/modules/services-systemd/main.py:82 msgid "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." +"Unknown systemd commands {command!s} and {suffix!s} for unit {name!s}." msgstr "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." #: src/modules/umount/main.py:40 msgid "Unmount file systems." -msgstr "Unmount file systems." +msgstr "" #: src/modules/unpackfs/main.py:44 msgid "Filling up filesystems." -msgstr "Filling up filesystems." +msgstr "" #: src/modules/unpackfs/main.py:257 msgid "rsync failed with error code {}." -msgstr "rsync failed with error code {}." +msgstr "" #: src/modules/unpackfs/main.py:302 msgid "Unpacking image {}/{}, file {}/{}" -msgstr "Unpacking image {}/{}, file {}/{}" +msgstr "" #: src/modules/unpackfs/main.py:317 msgid "Starting to unpack {}" -msgstr "Starting to unpack {}" +msgstr "" #: src/modules/unpackfs/main.py:326 src/modules/unpackfs/main.py:448 msgid "Failed to unpack image \"{}\"" -msgstr "Failed to unpack image \"{}\"" +msgstr "" #: src/modules/unpackfs/main.py:415 msgid "No mount point for root partition" -msgstr "No mount point for root partition" +msgstr "" #: src/modules/unpackfs/main.py:416 msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" -msgstr "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" +msgstr "" #: src/modules/unpackfs/main.py:421 msgid "Bad mount point for root partition" -msgstr "Bad mount point for root partition" +msgstr "" #: src/modules/unpackfs/main.py:422 msgid "rootMountPoint is \"{}\", which does not exist, doing nothing" -msgstr "rootMountPoint is \"{}\", which does not exist, doing nothing" +msgstr "" #: src/modules/unpackfs/main.py:438 src/modules/unpackfs/main.py:442 #: src/modules/unpackfs/main.py:462 msgid "Bad unsquash configuration" -msgstr "Bad unsquash configuration" +msgstr "" #: src/modules/unpackfs/main.py:439 msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel" -msgstr "The filesystem for \"{}\" ({}) is not supported by your current kernel" +msgstr "" #: src/modules/unpackfs/main.py:443 msgid "The source filesystem \"{}\" does not exist" -msgstr "The source filesystem \"{}\" does not exist" +msgstr "" #: src/modules/unpackfs/main.py:449 msgid "" "Failed to find unsquashfs, make sure you have the squashfs-tools package " "installed" msgstr "" -"Failed to find unsquashfs, make sure you have the squashfs-tools package " -"installed" #: src/modules/unpackfs/main.py:463 msgid "The destination \"{}\" in the target system is not a directory" -msgstr "The destination \"{}\" in the target system is not a directory" +msgstr "" #: src/modules/displaymanager/main.py:523 msgid "Cannot write KDM configuration file" -msgstr "Cannot write KDM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:524 msgid "KDM config file {!s} does not exist" -msgstr "KDM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:585 msgid "Cannot write LXDM configuration file" -msgstr "Cannot write LXDM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:586 msgid "LXDM config file {!s} does not exist" -msgstr "LXDM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:669 msgid "Cannot write LightDM configuration file" -msgstr "Cannot write LightDM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:670 msgid "LightDM config file {!s} does not exist" -msgstr "LightDM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:744 msgid "Cannot configure LightDM" -msgstr "Cannot configure LightDM" +msgstr "" #: src/modules/displaymanager/main.py:745 msgid "No LightDM greeter installed." -msgstr "No LightDM greeter installed." +msgstr "" #: src/modules/displaymanager/main.py:776 msgid "Cannot write SLIM configuration file" -msgstr "Cannot write SLIM configuration file" +msgstr "" #: src/modules/displaymanager/main.py:777 msgid "SLIM config file {!s} does not exist" -msgstr "SLIM config file {!s} does not exist" +msgstr "" #: src/modules/displaymanager/main.py:903 msgid "No display managers selected for the displaymanager module." -msgstr "No display managers selected for the displaymanager module." +msgstr "" #: src/modules/displaymanager/main.py:904 msgid "" "The displaymanagers list is empty or undefined in bothglobalstorage and " "displaymanager.conf." msgstr "" -"The displaymanagers list is empty or undefined in bothglobalstorage and " -"displaymanager.conf." #: src/modules/displaymanager/main.py:986 msgid "Display manager configuration was incomplete" -msgstr "Display manager configuration was incomplete" +msgstr "" #: src/modules/initcpiocfg/main.py:37 msgid "Configuring mkinitcpio." -msgstr "Configuring mkinitcpio." +msgstr "" #: src/modules/initcpiocfg/main.py:210 #: src/modules/luksopenswaphookcfg/main.py:100 @@ -216,139 +209,131 @@ msgstr "Configuring mkinitcpio." #: src/modules/fstab/main.py:339 src/modules/localecfg/main.py:145 #: src/modules/networkcfg/main.py:49 msgid "No root mount point is given for
{!s}
to use." -msgstr "No root mount point is given for
{!s}
to use." +msgstr "" #: src/modules/luksopenswaphookcfg/main.py:35 msgid "Configuring encrypted swap." -msgstr "Configuring encrypted swap." +msgstr "" #: src/modules/rawfs/main.py:35 msgid "Installing data." -msgstr "Installing data." +msgstr "" #: src/modules/services-openrc/main.py:38 msgid "Configure OpenRC services" -msgstr "Configure OpenRC services" +msgstr "" #: src/modules/services-openrc/main.py:66 msgid "Cannot add service {name!s} to run-level {level!s}." -msgstr "Cannot add service {name!s} to run-level {level!s}." +msgstr "" #: src/modules/services-openrc/main.py:68 msgid "Cannot remove service {name!s} from run-level {level!s}." -msgstr "Cannot remove service {name!s} from run-level {level!s}." +msgstr "" #: src/modules/services-openrc/main.py:70 msgid "" "Unknown service-action {arg!s} for service {name!s} in run-" "level {level!s}." msgstr "" -"Unknown service-action {arg!s} for service {name!s} in run-" -"level {level!s}." #: src/modules/services-openrc/main.py:103 msgid "" "rc-update {arg!s} call in chroot returned error code {num!s}." msgstr "" -"rc-update {arg!s} call in chroot returned error code {num!s}." #: src/modules/services-openrc/main.py:110 msgid "Target runlevel does not exist" -msgstr "Target runlevel does not exist" +msgstr "" #: src/modules/services-openrc/main.py:111 msgid "" "The path for runlevel {level!s} is {path!s}, which does not " "exist." msgstr "" -"The path for runlevel {level!s} is {path!s}, which does not " -"exist." #: src/modules/services-openrc/main.py:119 msgid "Target service does not exist" -msgstr "Target service does not exist" +msgstr "" #: src/modules/services-openrc/main.py:120 msgid "" -"The path for service {name!s} is {path!s}, which does not " -"exist." +"The path for service {name!s} is {path!s}, which does not exist." msgstr "" -"The path for service {name!s} is {path!s}, which does not " -"exist." #: src/modules/plymouthcfg/main.py:36 msgid "Configure Plymouth theme" -msgstr "Configure Plymouth theme" +msgstr "" #: src/modules/packages/main.py:59 src/modules/packages/main.py:68 #: src/modules/packages/main.py:78 msgid "Install packages." -msgstr "Install packages." +msgstr "" #: src/modules/packages/main.py:66 #, python-format msgid "Processing packages (%(count)d / %(total)d)" -msgstr "Processing packages (%(count)d / %(total)d)" +msgstr "" #: src/modules/packages/main.py:71 #, python-format msgid "Installing one package." msgid_plural "Installing %(num)d packages." -msgstr[0] "Installing one package." -msgstr[1] "Installing %(num)d packages." +msgstr[0] "" +msgstr[1] "" #: src/modules/packages/main.py:74 #, python-format msgid "Removing one package." msgid_plural "Removing %(num)d packages." -msgstr[0] "Removing one package." -msgstr[1] "Removing %(num)d packages." +msgstr[0] "" +msgstr[1] "" #: src/modules/bootloader/main.py:51 msgid "Install bootloader." -msgstr "Install bootloader." +msgstr "" #: src/modules/hwclock/main.py:35 msgid "Setting hardware clock." -msgstr "Setting hardware clock." +msgstr "" #: src/modules/dracut/main.py:36 msgid "Creating initramfs with dracut." -msgstr "Creating initramfs with dracut." +msgstr "" #: src/modules/dracut/main.py:58 msgid "Failed to run dracut on the target" -msgstr "Failed to run dracut on the target" +msgstr "" #: src/modules/dracut/main.py:59 msgid "The exit code was {}" -msgstr "The exit code was {}" +msgstr "" #: src/modules/initramfscfg/main.py:41 msgid "Configuring initramfs." -msgstr "Configuring initramfs." +msgstr "" #: src/modules/openrcdmcryptcfg/main.py:34 msgid "Configuring OpenRC dmcrypt service." -msgstr "Configuring OpenRC dmcrypt service." +msgstr "" #: src/modules/fstab/main.py:38 msgid "Writing fstab." -msgstr "Writing fstab." +msgstr "" #: src/modules/dummypython/main.py:44 msgid "Dummy python job." -msgstr "Dummy python job." +msgstr "" #: src/modules/dummypython/main.py:46 src/modules/dummypython/main.py:102 #: src/modules/dummypython/main.py:103 msgid "Dummy python step {}" -msgstr "Dummy python step {}" +msgstr "" #: src/modules/localecfg/main.py:39 msgid "Configuring locales." -msgstr "Configuring locales." +msgstr "" #: src/modules/networkcfg/main.py:37 msgid "Saving network configuration." -msgstr "Saving network configuration." +msgstr "" diff --git a/src/modules/dummypythonqt/lang/dummypythonqt.pot b/src/modules/dummypythonqt/lang/dummypythonqt.pot index 13cc7ada3..dd6d2171e 100644 --- a/src/modules/dummypythonqt/lang/dummypythonqt.pot +++ b/src/modules/dummypythonqt/lang/dummypythonqt.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-18 15:42+0200\n" +"POT-Creation-Date: 2020-08-09 20:56+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" From f07c6ed876139d567767eb7c78756ec323891be0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 21:00:40 +0200 Subject: [PATCH 83/91] i18n: drop pythonqt translations from the tooling --- ci/txcheck.sh | 4 ++-- ci/txpull.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/txcheck.sh b/ci/txcheck.sh index 9ce8f0c30..229cc8a73 100644 --- a/ci/txcheck.sh +++ b/ci/txcheck.sh @@ -29,7 +29,7 @@ ### END USAGE # The files that are translated; should match the contents of .tx/config -TX_FILE_LIST="lang/calamares_en.ts lang/python.pot src/modules/dummypythonqt/lang/dummypythonqt.pot calamares.desktop" +TX_FILE_LIST="lang/calamares_en.ts lang/python.pot calamares.desktop" ### COMMAND ARGUMENTS # @@ -125,7 +125,7 @@ tx_sum() # Remove linenumbers from .ts (XML) and .pot sed -i'' -e '/ calamares.desktop.new mv calamares.desktop.new calamares.desktop } From afebe8211ab8c671c04c808782242f8160b24a4b Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 21:01:44 +0200 Subject: [PATCH 84/91] Changes: pre-release housekeeping --- CHANGES | 14 +++++++++++--- CMakeLists.txt | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 4d5e19888..cc3a596f9 100644 --- a/CHANGES +++ b/CHANGES @@ -3,7 +3,7 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. -# 3.2.28 (unreleased) # +# 3.2.28 (2020-08-09) # This release contains contributions from (alphabetically by first name): - Anke Boersma @@ -14,9 +14,11 @@ This release contains contributions from (alphabetically by first name): - A new object *Network* is available to QML modules in `io.calamares.core`. It exposes network status through the *hasInternet* property. - Welcome to Tajik translations. The Tajik language has quickly reached - 100% completion. + 100% completion. Thanks Victor! - Welcome to [Interlingue](https://en.wikipedia.org/wiki/Interlingue). - The translation is at an early stage. + The translation is at an early stage. Qt does not support language + code *ie* though, so it may take some time to be integrated (much + like Esperanto wasn't supported until Qt 5.12). ## Modules ## - The *locale* module has been completely redone on the inside. @@ -31,9 +33,15 @@ This release contains contributions from (alphabetically by first name): timezone setting -- this can be useful to avoid both hard-coding an initial zone and doing extra GeoIP lookups, in the case where the live system already does so. #1391 + - The *locale* and *localeq* modules have additional machinery for + timezone lookups; please report cases where clicking on the map + returns an obviously bogus timezone (up until this release, for + instance, Cape Town). - The *users* module no longer accepts `root` as a username. #1462 - The *keyboardq* module is now more inline with the look of the rest of the Calamares modules, use of a background image is removed. + - The *grubcfg* module now understands `/etc/default/grub.d`. #1457 + # 3.2.27 (2020-07-11) # diff --git a/CMakeLists.txt b/CMakeLists.txt index 10f314856..163019965 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ project( CALAMARES VERSION 3.2.28 LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development ### OPTIONS # From 4c8a624c0b88848aa0a647bf727fae73f7ae12fc Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 21:03:58 +0200 Subject: [PATCH 85/91] ci: update the GPG key ID used for signing --- ci/RELEASE.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/RELEASE.sh b/ci/RELEASE.sh index 69aaec0be..399aa98c9 100755 --- a/ci/RELEASE.sh +++ b/ci/RELEASE.sh @@ -119,7 +119,7 @@ test -n "$V" || { echo "Could not obtain version in $BUILDDIR." ; exit 1 ; } # # This is the signing key ID associated with the GitHub account adriaandegroot, # which is used to create all "verified" tags in the Calamares repo. -KEY_ID="61A7D26277E4D0DB" +KEY_ID="CFDDC96F12B1915C" git tag -u "$KEY_ID" -m "Release v$V" "v$V" || { echo "Could not sign tag v$V." ; exit 1 ; } ### Create the tarball From c1d7d3daa6c2c42fc3804718634d9b050dfc979f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 21:10:24 +0200 Subject: [PATCH 86/91] ci: put a space after directory names in user-visible messages - this makes is much easier to double-click-select the directory, since the . isn't attached and won't be selected along with the directory anymore. --- ci/RELEASE.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/RELEASE.sh b/ci/RELEASE.sh index 399aa98c9..82c77c3e2 100755 --- a/ci/RELEASE.sh +++ b/ci/RELEASE.sh @@ -78,7 +78,7 @@ if test "x$BUILD_DEFAULT" = "xtrue" ; then rm -rf "$BUILDDIR" mkdir "$BUILDDIR" || { echo "Could not create build directory." ; exit 1 ; } ( cd "$BUILDDIR" && cmake .. && make -j4 ) || { echo "Could not perform test-build in $BUILDDIR." ; exit 1 ; } - ( cd "$BUILDDIR" && make test ) || { echo "Tests failed in $BUILDDIR." ; exit 1 ; } + ( cd "$BUILDDIR" && make test ) || { echo "Tests failed in $BUILDDIR ." ; exit 1 ; } fi ### Build with clang @@ -95,7 +95,7 @@ if test "x$BUILD_CLANG" = "xtrue" ; then fi if test "x$BUILD_ONLY" = "xtrue" ; then - echo "Builds completed, release stopped. Build remains in $BUILDDIR." + echo "Builds completed, release stopped. Build remains in $BUILDDIR ." exit 1 fi @@ -106,14 +106,14 @@ else # Presumably -B was given; just do the cmake part rm -rf "$BUILDDIR" mkdir "$BUILDDIR" || { echo "Could not create build directory." ; exit 1 ; } - ( cd "$BUILDDIR" && cmake .. ) || { echo "Could not run cmake in $BUILDDIR." ; exit 1 ; } + ( cd "$BUILDDIR" && cmake .. ) || { echo "Could not run cmake in $BUILDDIR ." ; exit 1 ; } fi ### Get version number for this release # # V=$( cd "$BUILDDIR" && make show-version | grep ^CALAMARES_VERSION | sed s/^[A-Z_]*=// ) -test -n "$V" || { echo "Could not obtain version in $BUILDDIR." ; exit 1 ; } +test -n "$V" || { echo "Could not obtain version in $BUILDDIR ." ; exit 1 ; } ### Create signed tag # @@ -139,7 +139,7 @@ TMPDIR=$(mktemp -d --suffix="-calamares-$D") test -d "$TMPDIR" || { echo "Could not create tarball-build directory." ; exit 1 ; } tar xzf "$TAR_FILE" -C "$TMPDIR" || { echo "Could not unpack tarball." ; exit 1 ; } test -d "$TMPDIR/$TAR_V" || { echo "Tarball did not contain source directory." ; exit 1 ; } -( cd "$TMPDIR/$TAR_V" && cmake . && make -j4 && make test ) || { echo "Tarball build failed in $TMPDIR." ; exit 1 ; } +( cd "$TMPDIR/$TAR_V" && cmake . && make -j4 && make test ) || { echo "Tarball build failed in $TMPDIR ." ; exit 1 ; } ### Cleanup # From e2bf62a64b388cc006cc9f8ce57132b0029dfc0e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 22:35:10 +0200 Subject: [PATCH 87/91] [libcalamares] Repair test for old Qt - Qt 5.11 and early 5.12 just don't support Esperanto at all --- src/libcalamares/locale/Tests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index b755a61a5..6414e2ebf 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -120,10 +120,11 @@ LocaleTests::testEsperanto() { #if QT_VERSION < QT_VERSION_CHECK( 5, 12, 2 ) QCOMPARE( QLocale( "eo" ).language(), QLocale::C ); + QCOMPARE( QLocale( QLocale::Esperanto ).language(), QLocale::English ); #else QCOMPARE( QLocale( "eo" ).language(), QLocale::Esperanto ); -#endif QCOMPARE( QLocale( QLocale::Esperanto ).language(), QLocale::Esperanto ); // Probably fails on 5.12, too +#endif } void From 4a3378d8b93abe4235ffb4d2e38a83f8cc09b85f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 23:01:59 +0200 Subject: [PATCH 88/91] [libcalamares] Repair tests around save/load YAML round-trip - QStringList doesn't round-trip correctly; add a test to demonstrate that. - Fix existing test to **not** use QStringList, but QVariantList (of strings), which is how other code would use it. The above is **kind** of moot because nothing uses the YAML-save function, but it might. While here, fix another test: YAML-loading can load JSON just fine. --- src/libcalamares/Tests.cpp | 42 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/Tests.cpp b/src/libcalamares/Tests.cpp index bf419d721..53bedc544 100644 --- a/src/libcalamares/Tests.cpp +++ b/src/libcalamares/Tests.cpp @@ -38,6 +38,7 @@ private Q_SLOTS: void testGSModify(); void testGSLoadSave(); void testGSLoadSave2(); + void testGSLoadSaveYAMLStringList(); }; void @@ -75,7 +76,13 @@ TestLibCalamares::testGSLoadSave() gs.insert( "derp", 17 ); gs.insert( "cow", "moo" ); - gs.insert( "dwarfs", QStringList { "dopey", "sneezy" } ); + + QVariantList l; + for ( const auto& s : QStringList { "dopey", "sneezy" } ) + { + l.append( s ); + } + gs.insert( "dwarfs", l ); QCOMPARE( gs.count(), 3 ); @@ -93,8 +100,12 @@ TestLibCalamares::testGSLoadSave() QCOMPARE( gs3.count(), 3 ); QCOMPARE( gs3.data(), gs.data() ); + // YAML can load as JSON! + QVERIFY( gs3.loadYaml( jsonfilename ) ); + QCOMPARE( gs3.count(), 3 ); + QCOMPARE( gs3.data(), gs.data() ); + // Failures in loading - QVERIFY( !gs3.loadYaml( jsonfilename ) ); QVERIFY( !gs3.loadJson( yamlfilename ) ); Calamares::GlobalStorage gs4; @@ -139,6 +150,33 @@ TestLibCalamares::testGSLoadSave2() QCOMPARE( gs2.value( key ).type(), QVariant::List ); } +void +TestLibCalamares::testGSLoadSaveYAMLStringList() +{ + Calamares::GlobalStorage gs; + const QString yamlfilename( "gs.test.yaml" ); + + gs.insert( "derp", 17 ); + gs.insert( "cow", "moo" ); + gs.insert( "dwarfs", QStringList { "happy", "dopey", "sleepy", "sneezy", "doc", "thorin", "balin" } ); + + QCOMPARE( gs.count(), 3 ); + QCOMPARE( gs.value( "dwarfs" ).toList().count(), 7 ); // There's seven dwarfs, right? + QVERIFY( gs.value( "dwarfs" ).toStringList().contains( "thorin" ) ); + QVERIFY( !gs.value( "dwarfs" ).toStringList().contains( "gimli" ) ); + + + QVERIFY( gs.saveYaml( yamlfilename ) ); + + Calamares::GlobalStorage gs2; + QCOMPARE( gs2.count(), 0 ); + QVERIFY( gs2.loadYaml( yamlfilename ) ); + QCOMPARE( gs2.count(), 3 ); + QEXPECT_FAIL( "", "QStringList doesn't write out nicely", Continue ); + QCOMPARE( gs2.value( "dwarfs" ).toList().count(), 7 ); // There's seven dwarfs, right? + QCOMPARE( gs2.value( "dwarfs" ).toString(), QStringLiteral( "" ) ); // .. they're gone +} + QTEST_GUILESS_MAIN( TestLibCalamares ) From 296146e4f8c3b5f72472a9f38e3c014f14f56306 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 10 Aug 2020 09:32:50 +0200 Subject: [PATCH 89/91] [libcalamares] update SPDX licensing on generated files - the scripts are BSD-2-clause, - the generated files are CC0 (I'm not *100%* sure about the derived file CountryData_p.cpp, which lists countries and country codes -- it **is** extracted from CLDR data which is not CC0) --- src/libcalamares/locale/CountryData_p.cpp | 13 +++- src/libcalamares/locale/ZoneData_p.cxxtr | 24 +------ src/libcalamares/locale/cldr-extractor.py | 87 ++++++++--------------- src/libcalamares/locale/zone-extractor.py | 53 +++----------- 4 files changed, 57 insertions(+), 120 deletions(-) diff --git a/src/libcalamares/locale/CountryData_p.cpp b/src/libcalamares/locale/CountryData_p.cpp index 4382950ec..722ee2ba9 100644 --- a/src/libcalamares/locale/CountryData_p.cpp +++ b/src/libcalamares/locale/CountryData_p.cpp @@ -2,7 +2,13 @@ * * === This file is part of Calamares - === * -* This file is derived from CLDR data from Unicode, Inc. Applicable terms: +* SPDX-FileCopyrightText: 1991-2019 Unicode, Inc. +* SPDX-FileCopyrightText: 2019 Adriaan de Groot +* SPDX-License-Identifier: CC0 +* +* This file is derived from CLDR data from Unicode, Inc. Applicable terms +* are listed at http://unicode.org/copyright.html , of which the most +* important are: * * A. Unicode Copyright * 1. Copyright © 1991-2019 Unicode, Inc. All rights reserved. @@ -10,6 +16,11 @@ * Unicode Data Files ("DATA FILES") include all data files under the directories: * https://www.unicode.org/Public/ * C. Terms of Use +* 1. Certain documents and files on this website contain a legend indicating +* that "Modification is permitted." Any person is hereby authorized, +* without fee, to modify such documents and files to create derivative +* works conforming to the Unicode® Standard, subject to Terms and +* Conditions herein. * 2. Any person is hereby authorized, without fee, to view, use, reproduce, * and distribute all documents and files, subject to the Terms and * Conditions herein. diff --git a/src/libcalamares/locale/ZoneData_p.cxxtr b/src/libcalamares/locale/ZoneData_p.cxxtr index 4bfef3c0c..7bbcdf7f6 100644 --- a/src/libcalamares/locale/ZoneData_p.cxxtr +++ b/src/libcalamares/locale/ZoneData_p.cxxtr @@ -1,26 +1,8 @@ /* GENERATED FILE DO NOT EDIT * -* === This file is part of Calamares - === - * - * SPDX-FileCopyrightText: 2019 Adriaan de Groot - * - * Calamares is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Calamares is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Calamares. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - * License-Filename: LICENSE - * - * +* SPDX-FileCopyrightText: 2009 Arthur David Olson +* SPDX-FileCopyrightText: 2019 Adriaan de Groot +* SPDX-License-Identifier: CC0 * * This file is derived from zone.tab, which has its own copyright statement: * diff --git a/src/libcalamares/locale/cldr-extractor.py b/src/libcalamares/locale/cldr-extractor.py index f69407257..0291abf7d 100644 --- a/src/libcalamares/locale/cldr-extractor.py +++ b/src/libcalamares/locale/cldr-extractor.py @@ -1,45 +1,9 @@ #! /usr/bin/env python3 # # === This file is part of Calamares - === -# +# # SPDX-FileCopyrightText: 2019 Adriaan de Groot -# # SPDX-License-Identifier: BSD-2-Clause -# License-Filename: LICENSES/BSD2 -# -# -# -# Python3 script to scrape some data out of ICU CLDR supplemental data. -# -### BEGIN LICENSES -# -# Copyright 2019 Adriaan de Groot -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -### END LICENSES - -### BEGIN USAGE # """ Python3 script to scrape some data out of ICU CLDR supplemental data. @@ -126,7 +90,7 @@ class CountryData: self.country_code = "" self.language_enum = "AnyLanguage" self.country_enum = "AnyCountry" - + def __str__(self): if self.country_code: char0 = "'{!s}'".format(self.country_code[0]) @@ -134,18 +98,18 @@ class CountryData: else: char0 = "0" char1 = "0" - + return "{!s} QLocale::Language::{!s}, QLocale::Country::{!s}, {!s}, {!s} {!s},".format( "{", - self.language_enum, + self.language_enum, self.country_enum, - char0, + char0, char1, "}") # Must match type name below cpp_classname = "CountryData" - + # Must match the output format of __str__ above cpp_declaration = """ struct CountryData @@ -169,30 +133,30 @@ def extricate_subtags(l1, l2): return if '{ ?; ?;' not in l2: return - + # This is extremely crude "parsing" which chops up the string # by delimiter and then extracts some substring. l1_parts = l1.split("und_") l2_parts = l2.split(";") - + l1_first_quote = l1_parts[1].find('"') l1_code = l1_parts[1][:l1_first_quote] if len(l1_code) != 2: return - + l2_brace = l2_parts[2].find("{") l2_language = l2_parts[2][l2_brace+1:].strip() l2_brace = l2_parts[2].find("}") l2_country = l2_parts[2][:l2_brace-1].strip() - + # Handle mapped cases l2_language = language_mapper.get(l2_language, l2_language) l2_language = l2_language.replace(" ", "") - + # Handle mapped cases and then do a bunch of standard replacements. l2_country = country_mapper.get(l2_country, l2_country) l2_country = l2_country.replace(" ", "").replace("-", "").replace(".","").replace("&","And") - + return CountryData(l1_code, l2_language, l2_country) @@ -213,7 +177,7 @@ def read_subtags_file(): if l1: assert "likelySubtag" in l1, l1; assert "