From 4febe477cf82e1cf27f0c27a7a07822d8ba08131 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 11:53:35 +0200 Subject: [PATCH 01/16] [libcalamares] Add isEmpty() to TranslatedString - Massage the implementation a bit, don't insert a meaningless copy of the key as the untranslated message. - Add isEmpty() to check for presence of the untranslated message. - Document API. - Update tests. --- src/libcalamares/locale/Tests.cpp | 33 ++++++++++++++++++- .../locale/TranslatableConfiguration.cpp | 4 --- .../locale/TranslatableConfiguration.h | 16 ++++++++- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/libcalamares/locale/Tests.cpp b/src/libcalamares/locale/Tests.cpp index 664390511..fa84cd2fb 100644 --- a/src/libcalamares/locale/Tests.cpp +++ b/src/libcalamares/locale/Tests.cpp @@ -96,6 +96,10 @@ someLanguages() } +/** @brief Check consistency of test data + * Check that all the languages used in testing, are actually enabled + * in Calamares translations. + */ void LocaleTests::testTranslatableLanguages() { @@ -108,12 +112,19 @@ LocaleTests::testTranslatableLanguages() } } +/** @brief Test strings with no translations + */ void LocaleTests::testTranslatableConfig1() { + CalamaresUtils::Locale::TranslatedString ts0; + QVERIFY( ts0.isEmpty() ); + QCOMPARE( ts0.count(), 1 ); // the empty string + QCOMPARE( QLocale().name(), "C" ); // Otherwise plain get() is dubious CalamaresUtils::Locale::TranslatedString ts1( "Hello" ); QCOMPARE( ts1.count(), 1 ); + QVERIFY( !ts1.isEmpty() ); QCOMPARE( ts1.get(), "Hello" ); QCOMPARE( ts1.get( QLocale( "nl" ) ), "Hello" ); @@ -122,11 +133,14 @@ LocaleTests::testTranslatableConfig1() map.insert( "description", "description (no language)" ); CalamaresUtils::Locale::TranslatedString ts2( map, "description" ); QCOMPARE( ts2.count(), 1 ); + QVERIFY( !ts2.isEmpty() ); QCOMPARE( ts2.get(), "description (no language)" ); QCOMPARE( ts2.get( QLocale( "nl" ) ), "description (no language)" ); } +/** @bref Test strings with translations. + */ void LocaleTests::testTranslatableConfig2() { @@ -143,11 +157,22 @@ LocaleTests::testTranslatableConfig2() } } + // If there's no untranslated string in the map, it is considered empty + CalamaresUtils::Locale::TranslatedString ts0( map, "description" ); + QVERIFY( ts0.isEmpty() ); // Because no untranslated string + QCOMPARE( ts0.count(), + someLanguages().count() + 1 ); // But there are entries for the translations, plus an empty string + + // expand the map with untranslated entries + map.insert( QString( "description" ), "description (no language)" ); + map.insert( QString( "name" ), "name (no language)" ); + CalamaresUtils::Locale::TranslatedString ts1( map, "description" ); // The +1 is because "" is always also inserted QCOMPARE( ts1.count(), someLanguages().count() + 1 ); + QVERIFY( !ts1.isEmpty() ); - QCOMPARE( ts1.get(), "description" ); // it wasn't set + QCOMPARE( ts1.get(), "description (no language)" ); // it wasn't set QCOMPARE( ts1.get( QLocale( "nl" ) ), "description (language nl)" ); for ( const auto& language : someLanguages() ) { @@ -167,4 +192,10 @@ LocaleTests::testTranslatableConfig2() CalamaresUtils::Locale::TranslatedString ts2( map, "name" ); // We skipped dutch this time QCOMPARE( ts2.count(), someLanguages().count() ); + QVERIFY( !ts2.isEmpty() ); + + // This key doesn't exist + CalamaresUtils::Locale::TranslatedString ts3( map, "front" ); + QVERIFY( ts3.isEmpty() ); + QCOMPARE( ts3.count(), 1 ); // The empty string } diff --git a/src/libcalamares/locale/TranslatableConfiguration.cpp b/src/libcalamares/locale/TranslatableConfiguration.cpp index b3b5259c9..7493c836c 100644 --- a/src/libcalamares/locale/TranslatableConfiguration.cpp +++ b/src/libcalamares/locale/TranslatableConfiguration.cpp @@ -38,10 +38,6 @@ TranslatedString::TranslatedString( const QVariantMap& map, const QString& key ) { // Get the un-decorated value for the key QString value = CalamaresUtils::getString( map, key ); - if ( value.isEmpty() ) - { - value = key; - } m_strings[ QString() ] = value; for ( auto it = map.constKeyValueBegin(); it != map.constKeyValueEnd(); ++it ) diff --git a/src/libcalamares/locale/TranslatableConfiguration.h b/src/libcalamares/locale/TranslatableConfiguration.h index b2f598069..a055cbfbd 100644 --- a/src/libcalamares/locale/TranslatableConfiguration.h +++ b/src/libcalamares/locale/TranslatableConfiguration.h @@ -46,9 +46,23 @@ public: TranslatedString( const QString& string ); /// @brief Empty string TranslatedString() - : TranslatedString( QString() ) {} + : TranslatedString( QString() ) + { + } + /** @brief How many strings (translations) are there? + * + * This is always at least 1 (for the untranslated string), + * but may be more than 1 even when isEmpty() is true -- + * if there is no untranslated version, for instance. + */ int count() const { return m_strings.count(); } + /** @brief Consider this string empty? + * + * Only the state of the untranslated string is considered, + * so count() may be more than 1 even while the string is empty. + */ + bool isEmpty() const { return m_strings[ QString() ].isEmpty(); } /// @brief Gets the string in the current locale QString get() const; From 6e05a1ef0550e6f8026334383fb2b46754581920 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 12:02:29 +0200 Subject: [PATCH 02/16] [packagechooser] Load translated strings as well - This makes it possible to put the translations into the config file, and have them displayed when the Calamares language changes. --- .../packagechooser/PackageChooserViewStep.cpp | 26 +------------ src/modules/packagechooser/PackageModel.cpp | 38 +++++++++++++++++-- src/modules/packagechooser/PackageModel.h | 18 +++++++++ 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/modules/packagechooser/PackageChooserViewStep.cpp b/src/modules/packagechooser/PackageChooserViewStep.cpp index 4476eb9e6..a3b853b39 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.cpp +++ b/src/modules/packagechooser/PackageChooserViewStep.cpp @@ -232,31 +232,7 @@ PackageChooserViewStep::fillModel( const QVariantList& items ) continue; } - QString id = CalamaresUtils::getString( item_map, "id" ); - QString package = CalamaresUtils::getString( item_map, "package" ); - QString name = CalamaresUtils::getString( item_map, "name" ); - QString description = CalamaresUtils::getString( item_map, "description" ); - QString screenshot = CalamaresUtils::getString( item_map, "screenshot" ); - - if ( name.isEmpty() && id.isEmpty() ) - { - name = tr( "No product" ); - } - else if ( name.isEmpty() ) - { - cWarning() << "PackageChooser item" << id << "has an empty name."; - continue; - } - if ( description.isEmpty() ) - { - description = tr( "No description provided." ); - } - if ( screenshot.isEmpty() ) - { - screenshot = QStringLiteral( ":/images/no-selection.png" ); - } - - m_model->addPackage( PackageItem { id, package, name, description, screenshot } ); + m_model->addPackage( PackageItem( item_map ) ); } } diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index f133f4fbd..3283cbe6f 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -19,6 +19,7 @@ #include "PackageModel.h" #include "utils/Logger.h" +#include "utils/Variant.h" const NamedEnumTable< PackageChooserMode >& roleNames() @@ -73,6 +74,31 @@ PackageItem::PackageItem( const QString& a_id, { } +PackageItem::PackageItem::PackageItem( const QVariantMap& item_map ) + : id( CalamaresUtils::getString( item_map, "id" ) ) + , package( CalamaresUtils::getString( item_map, "package" ) ) + , name( CalamaresUtils::Locale::TranslatedString( item_map, "name" ) ) + , description( CalamaresUtils::Locale::TranslatedString( item_map, "description" ) ) + , screenshot( CalamaresUtils::getString( item_map, "screenshot" ) ) +{ + if ( name.isEmpty() && id.isEmpty() ) + { + name = QObject::tr( "No product" ); + } + else if ( name.isEmpty() ) + { + cWarning() << "PackageChooser item" << id << "has an empty name."; + } + if ( description.isEmpty() ) + { + description = QObject::tr( "No description provided." ); + } + if ( screenshot.isNull() ) + { + screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); + } +} + PackageListModel::PackageListModel( QObject* parent ) : QAbstractListModel( parent ) @@ -90,10 +116,14 @@ PackageListModel::~PackageListModel() {} void PackageListModel::addPackage( PackageItem&& p ) { - int c = m_packages.count(); - beginInsertRows( QModelIndex(), c, c ); - m_packages.append( p ); - endInsertRows(); + // Only add valid packages + if ( !p.name.isEmpty() ) + { + int c = m_packages.count(); + beginInsertRows( QModelIndex(), c, c ); + m_packages.append( p ); + endInsertRows(); + } } int diff --git a/src/modules/packagechooser/PackageModel.h b/src/modules/packagechooser/PackageModel.h index 68e19a25d..f42ff3123 100644 --- a/src/modules/packagechooser/PackageModel.h +++ b/src/modules/packagechooser/PackageModel.h @@ -56,12 +56,26 @@ struct PackageItem */ PackageItem( const QString& id, const QString& package, const QString& name, const QString& description ); + /** @brief Creates a PackageItem from given strings. + * + * Set all the text members and load the screenshot from the given + * @p screenshotPath, which may be a QRC path (:/path/in/qrc) or + * a filesystem path, whatever QPixmap understands. + */ PackageItem( const QString& id, const QString& package, const QString& name, const QString& description, const QString& screenshotPath ); + /** @brief Creates a PackageItem from a QVariantMap + * + * This is intended for use when loading PackageItems from a + * configuration map. It will look up the various keys in the map + * and handle translation strings as well. + */ + PackageItem( const QVariantMap& map ); + // TODO: implement this PackageItem fromAppStream( const QString& filename ); }; @@ -75,6 +89,10 @@ public: PackageListModel( QObject* parent ); virtual ~PackageListModel() override; + /** @brief Add a package @p to the model + * + * Only valid packages are added -- that is, they must have a name. + */ void addPackage( PackageItem&& p ); int rowCount( const QModelIndex& index ) const override; From 6c41151f80dcc9d41553a4f80ed4d4f6de989d88 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 12:38:28 +0200 Subject: [PATCH 03/16] [packagechooser] Just one screenshot, and mark package unused --- src/modules/packagechooser/PackageModel.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/packagechooser/PackageModel.h b/src/modules/packagechooser/PackageModel.h index f42ff3123..ce384096c 100644 --- a/src/modules/packagechooser/PackageModel.h +++ b/src/modules/packagechooser/PackageModel.h @@ -40,11 +40,10 @@ const NamedEnumTable< PackageChooserMode >& roleNames(); struct PackageItem { QString id; - // TODO: may need more than one + // FIXME: unused QString package; CalamaresUtils::Locale::TranslatedString name; CalamaresUtils::Locale::TranslatedString description; - // TODO: may be more than one QPixmap screenshot; /// @brief Create blank PackageItem From 8329d7d7dc8072d958fc460785d57f53b646acf3 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 13:34:32 +0200 Subject: [PATCH 04/16] CI: Add an AppData file --- io.calamares.calamares.appdata.xml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 io.calamares.calamares.appdata.xml diff --git a/io.calamares.calamares.appdata.xml b/io.calamares.calamares.appdata.xml new file mode 100644 index 000000000..355f485c2 --- /dev/null +++ b/io.calamares.calamares.appdata.xml @@ -0,0 +1,29 @@ + + + io.calamares.calamares.desktop + CC0-1.0 + GPL-3.0+ + Calamares + Calamares + Calamares + Calamares + Calamares Linux Installer + Calamares Linux Installer + Linux Installatieprogramma Calamares + +

Calamares is an installer program for Linux distributions.

+

Calamares is een installatieprogramma voor Linux distributies.

+
+ https://calamares.io + https://https://github.com/calamares/calamares/issues/ + https://github.com/calamares/calamares/wiki + + + Calamares Welcome + https://calamares.io/images/cal_640.png + + + + calamares + +
From beb5896fa237f3b6bf8045ee94a8fa7b0d7fcc11 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 14:33:57 +0200 Subject: [PATCH 05/16] [packagechooser] Start implementation of AppData loading - Doing a manual read of the XML, since existing appdata libraries don't seem to have a convenient entry for what I need. - Expand tests to loading AppData (currently, they fail). --- src/modules/packagechooser/CMakeLists.txt | 15 +++++ src/modules/packagechooser/PackageModel.cpp | 64 ++++++++++++++++++--- src/modules/packagechooser/PackageModel.h | 14 ++++- src/modules/packagechooser/Tests.cpp | 17 ++++++ src/modules/packagechooser/Tests.h | 1 + 5 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/modules/packagechooser/CMakeLists.txt b/src/modules/packagechooser/CMakeLists.txt index 4663ccce7..70a86a3bb 100644 --- a/src/modules/packagechooser/CMakeLists.txt +++ b/src/modules/packagechooser/CMakeLists.txt @@ -1,4 +1,15 @@ find_package( Qt5 COMPONENTS Core Gui Widgets REQUIRED ) +set( _extra_libraries "" ) + +### OPTIONAL AppData XML support in PackageModel +# +# +find_package(Qt5 COMPONENTS Xml) +if ( Qt5Xml_FOUND ) + add_definitions( -DHAVE_XML ) + list( APPEND _extra_libraries Qt5::Xml ) +endif() + calamares_add_plugin( packagechooser TYPE viewmodule @@ -13,6 +24,7 @@ calamares_add_plugin( packagechooser page_package.ui LINK_PRIVATE_LIBRARIES calamaresui + ${_extra_libraries} SHARED_LIB ) @@ -23,8 +35,11 @@ if( ECM_FOUND AND BUILD_TESTING ) packagechoosertest LINK_LIBRARIES ${CALAMARES_LIBRARIES} + calamares_viewmodule_packagechooser Qt5::Core Qt5::Test + Qt5::Gui + ${_extra_libraries} ) calamares_automoc( packagechoosertest) endif() diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index 3283cbe6f..f13564d5e 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -21,6 +21,11 @@ #include "utils/Logger.h" #include "utils/Variant.h" +#ifdef HAVE_XML +#include +#include +#endif + const NamedEnumTable< PackageChooserMode >& roleNames() { @@ -41,13 +46,6 @@ roleNames() return names; } -PackageItem -PackageItem::fromAppStream( const QString& filename ) -{ - // TODO: implement this - return PackageItem {}; -} - PackageItem::PackageItem() {} PackageItem::PackageItem( const QString& a_id, @@ -99,6 +97,56 @@ PackageItem::PackageItem::PackageItem( const QVariantMap& item_map ) } } +#ifdef HAVE_XML +QDomDocument +loadAppData( const QString& fileName ) +{ + QFile file( fileName ); + if ( !file.open( QIODevice::ReadOnly ) ) + { + return QDomDocument(); + } + QDomDocument doc( "AppData" ); + if ( !doc.setContent( &file ) ) + { + file.close(); + return QDomDocument(); + } + file.close(); + return doc; +} + +QString +getChildText( const QDomNode& n, const QString& tagName ) +{ + QDomElement e = n.firstChildElement( tagName ); + return e.isNull() ? QString() : e.text(); +} +#endif + +PackageItem +PackageItem::fromAppData( const QString& fileName ) +{ +#ifdef HAVE_XML + QDomDocument doc = loadAppData( fileName ); + if ( doc.isNull() ) + { + return PackageItem(); + } + + QDomElement componentNode = doc.documentElement(); + if ( !componentNode.isNull() && componentNode.tagName() == "component" ) + { + QString id = getChildText( componentNode, "id" ); + cDebug() << "Got AppData id" << id; + } + + return PackageItem(); +#else + return PackageItem(); +#endif +} + PackageListModel::PackageListModel( QObject* parent ) : QAbstractListModel( parent ) @@ -117,7 +165,7 @@ void PackageListModel::addPackage( PackageItem&& p ) { // Only add valid packages - if ( !p.name.isEmpty() ) + if ( p.isValid() ) { int c = m_packages.count(); beginInsertRows( QModelIndex(), c, c ); diff --git a/src/modules/packagechooser/PackageModel.h b/src/modules/packagechooser/PackageModel.h index ce384096c..7e79b98f1 100644 --- a/src/modules/packagechooser/PackageModel.h +++ b/src/modules/packagechooser/PackageModel.h @@ -75,8 +75,18 @@ struct PackageItem */ PackageItem( const QVariantMap& map ); - // TODO: implement this - PackageItem fromAppStream( const QString& filename ); + /** @brief Is this item valid? + * + * A valid item has an untranslated name available. + */ + bool isValid() const { return !name.isEmpty(); } + + /** @brief Loads an AppData XML file and returns a PackageItem + * + * Requires XML support in libcalamares, if not present will + * return invalid PackageItems. + */ + static PackageItem fromAppData( const QString& filename ); }; using PackageList = QVector< PackageItem >; diff --git a/src/modules/packagechooser/Tests.cpp b/src/modules/packagechooser/Tests.cpp index c016f1808..6cbab8e38 100644 --- a/src/modules/packagechooser/Tests.cpp +++ b/src/modules/packagechooser/Tests.cpp @@ -18,6 +18,8 @@ #include "Tests.h" +#include "PackageModel.h" + #include QTEST_GUILESS_MAIN( PackageChooserTests ) @@ -36,3 +38,18 @@ PackageChooserTests::testBogus() { QVERIFY( true ); } + +void +PackageChooserTests::testAppData() +{ + // Path from the build-dir + QString appdataName( "../io.calamares.calamares.appdata.xml" ); + QVERIFY( QFile::exists( appdataName ) ); + + PackageItem p = PackageItem::fromAppData( appdataName ); +#ifdef HAVE_XML + QVERIFY( p.isValid() ); +#else + QVERIFY( !p.isValid() ); +#endif +} diff --git a/src/modules/packagechooser/Tests.h b/src/modules/packagechooser/Tests.h index bc257f5a5..62efe92cc 100644 --- a/src/modules/packagechooser/Tests.h +++ b/src/modules/packagechooser/Tests.h @@ -31,6 +31,7 @@ public: private Q_SLOTS: void initTestCase(); void testBogus(); + void testAppData(); }; #endif From 6821b14d009b92d956f9178232eb24ca3bf4f8cc Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 14:41:40 +0200 Subject: [PATCH 06/16] [packagechooser] Enable logging in tests --- src/modules/packagechooser/PackageModel.cpp | 4 ++++ src/modules/packagechooser/Tests.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index f13564d5e..9a572f036 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -128,6 +128,8 @@ PackageItem PackageItem::fromAppData( const QString& fileName ) { #ifdef HAVE_XML + cDebug() << "Loading AppData XML from" << fileName; + QDomDocument doc = loadAppData( fileName ); if ( doc.isNull() ) { @@ -143,6 +145,8 @@ PackageItem::fromAppData( const QString& fileName ) return PackageItem(); #else + cWarning() << "Loading AppData XML is not supported."; + return PackageItem(); #endif } diff --git a/src/modules/packagechooser/Tests.cpp b/src/modules/packagechooser/Tests.cpp index 6cbab8e38..d9cbbf4ed 100644 --- a/src/modules/packagechooser/Tests.cpp +++ b/src/modules/packagechooser/Tests.cpp @@ -20,6 +20,8 @@ #include "PackageModel.h" +#include "utils/Logger.h" + #include QTEST_GUILESS_MAIN( PackageChooserTests ) @@ -31,6 +33,7 @@ PackageChooserTests::~PackageChooserTests() {} void PackageChooserTests::initTestCase() { + Logger::setupLogLevel( Logger::LOGDEBUG ); } void From 431c4de77fe2c7d7647be00e5051694c5a2b7805 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 15:19:20 +0200 Subject: [PATCH 07/16] [packagechooser] Use GUI for tests - Because we'll be creating Pixmaps, we need to have a GUI main. --- src/modules/packagechooser/Tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/packagechooser/Tests.cpp b/src/modules/packagechooser/Tests.cpp index d9cbbf4ed..e15397613 100644 --- a/src/modules/packagechooser/Tests.cpp +++ b/src/modules/packagechooser/Tests.cpp @@ -24,7 +24,7 @@ #include -QTEST_GUILESS_MAIN( PackageChooserTests ) +QTEST_MAIN( PackageChooserTests ) PackageChooserTests::PackageChooserTests() {} From fb547364c73c8d36e5b6ed0d2c74ded1e3447513 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 15:36:34 +0200 Subject: [PATCH 08/16] [packagechooser] Complete parsing of AppData - Document all the static inline methods that do the work - Fill up a QVariantMap from , and

elements, and use that to initialize the PackageItem. --- src/modules/packagechooser/PackageModel.cpp | 152 +++++++++++++++++++- 1 file changed, 147 insertions(+), 5 deletions(-) diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index 9a572f036..29890ee9b 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -23,6 +23,7 @@ #ifdef HAVE_XML #include +#include #include #endif @@ -98,7 +99,12 @@ PackageItem::PackageItem::PackageItem( const QVariantMap& item_map ) } #ifdef HAVE_XML -QDomDocument +/** @brief try to load the given @p fileName XML document + * + * Returns a QDomDocument, which will be valid iff the file can + * be read and contains valid XML data. + */ +static inline QDomDocument loadAppData( const QString& fileName ) { QFile file( fileName ); @@ -116,12 +122,138 @@ loadAppData( const QString& fileName ) return doc; } -QString +/** @brief gets the text of child element @p tagName + */ +static inline QString getChildText( const QDomNode& n, const QString& tagName ) { QDomElement e = n.firstChildElement( tagName ); return e.isNull() ? QString() : e.text(); } + +/** @brief Gets a suitable screenshot path + * + * The element contains zero or more + * elements, which can have a *type* associated with them. + * Scan the screenshot elements, return the path + * for the one labeled with type=default or, if there is no + * default, the first element. + */ +static inline QString +getScreenshotPath( const QDomNode& n ) +{ + QDomElement shotsNode = n.firstChildElement( "screenshots" ); + if ( shotsNode.isNull() ) + { + return QString(); + } + + const QDomNodeList shotList = shotsNode.childNodes(); + int firstScreenshot = -1; // Use which screenshot node? + for ( int i = 0; i < shotList.count(); ++i ) + { + if ( !shotList.at( i ).isElement() ) + { + continue; + } + QDomElement e = shotList.at( i ).toElement(); + if ( e.tagName() != "screenshot" ) + { + continue; + } + // If none has the "type=default" attribute, use the first one + if ( firstScreenshot < 0 ) + { + firstScreenshot = i; + } + // But type=default takes precedence. + if ( e.hasAttribute( "type" ) && e.attribute( "type" ) == "default" ) + { + firstScreenshot = i; + break; + } + } + + if ( firstScreenshot >= 0 ) + { + return shotList.at( firstScreenshot ).firstChildElement( "image" ).text(); + } + + return QString(); +} + +/** @brief Returns language of the given element @p e + * + * Transforms the attribute value for xml:lang to something + * suitable for TranslatedString (e.g. [lang]). + */ +static inline QString +getLanguage( const QDomElement& e ) +{ + QString language = e.attribute( "xml:lang" ); + if ( !language.isEmpty() ) + { + language.replace( '-', '_' ); + language.prepend( '[' ); + language.append( ']' ); + } + return language; +} + +/** @brief Scan the list of @p children for @p tagname elements and add them to the map + * + * Uses @p mapname instead of @p tagname for the entries in map @p m + * to allow renaming from XML to map keys (in particular for + * TranslatedString). Also transforms xml:lang attributes to suitable + * key-decorations on @p mapname. + */ +static inline void +fillMap( QVariantMap& m, const QDomNodeList& children, const QString& tagname, const QString& mapname ) +{ + for ( int i = 0; i < children.count(); ++i ) + { + if ( !children.at( i ).isElement() ) + { + continue; + } + + QDomElement e = children.at( i ).toElement(); + if ( e.tagName() != tagname ) + { + continue; + } + + m[ mapname + getLanguage( e ) ] = e.text(); + } +} + +/** @brief gets the and elements +* +* Builds up a map of the elements (which may have a *lang* +* attribute to indicate translations and paragraphs of the +* element (also with lang). Uses the

+* elements to supplement the description if no description +* is available for a given language. +* +* Returns a map with keys suitable for use by TranslatedString. +*/ +static inline QVariantMap +getNameAndSummary( const QDomNode& n ) +{ + QVariantMap m; + + const QDomNodeList children = n.childNodes(); + fillMap( m, children, "name", "name" ); + fillMap( m, children, "summary", "description" ); + + const QDomElement description = n.firstChildElement( "description" ); + if ( !description.isNull() ) + { + fillMap( m, description.childNodes(), "p", "description" ); + } + + return m; +} #endif PackageItem @@ -129,7 +261,7 @@ PackageItem::fromAppData( const QString& fileName ) { #ifdef HAVE_XML cDebug() << "Loading AppData XML from" << fileName; - + QDomDocument doc = loadAppData( fileName ); if ( doc.isNull() ) { @@ -140,13 +272,23 @@ PackageItem::fromAppData( const QString& fileName ) if ( !componentNode.isNull() && componentNode.tagName() == "component" ) { QString id = getChildText( componentNode, "id" ); - cDebug() << "Got AppData id" << id; + if ( id.isEmpty() ) + { + return PackageItem(); + } + + QString screenshotPath = getScreenshotPath( componentNode ); + + QVariantMap map = getNameAndSummary( componentNode ); + map.insert( "id", id ); + map.insert( "screenshot", screenshotPath ); + return PackageItem( map ); } return PackageItem(); #else cWarning() << "Loading AppData XML is not supported."; - + return PackageItem(); #endif } From 9a8b2c5a1e189a1870b7ccec49f01b7fedc1d254 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 15:45:14 +0200 Subject: [PATCH 09/16] [packagechooser] CHeck that AppData load was succesful --- src/modules/packagechooser/Tests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/packagechooser/Tests.cpp b/src/modules/packagechooser/Tests.cpp index e15397613..5dec8a288 100644 --- a/src/modules/packagechooser/Tests.cpp +++ b/src/modules/packagechooser/Tests.cpp @@ -52,6 +52,13 @@ PackageChooserTests::testAppData() PackageItem p = PackageItem::fromAppData( appdataName ); #ifdef HAVE_XML QVERIFY( p.isValid() ); + QCOMPARE( p.id, "io.calamares.calamares.desktop" ); + QCOMPARE( p.name.get(), "Calamares" ); + // The entry has precedence + QCOMPARE( p.description.get(), "Calamares is an installer program for Linux distributions." ); + // .. but en_GB doesn't have an entry in description, so uses + QCOMPARE( p.description.get( QLocale( "en_GB" ) ), "Calamares Linux Installer" ); + QCOMPARE( p.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); #else QVERIFY( !p.isValid() ); #endif From d72391942ffe6c0bf34482fd1c55566061b45618 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 15:55:27 +0200 Subject: [PATCH 10/16] [packagechooser] Implement AppData loading - An item can refer to *appdata* and load that, or provide the data in the config file itself. - Fix documentation about translations. --- .../packagechooser/PackageChooserViewStep.cpp | 9 ++++++- .../packagechooser/packagechooser.conf | 24 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/modules/packagechooser/PackageChooserViewStep.cpp b/src/modules/packagechooser/PackageChooserViewStep.cpp index a3b853b39..fadacf8d4 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.cpp +++ b/src/modules/packagechooser/PackageChooserViewStep.cpp @@ -232,7 +232,14 @@ PackageChooserViewStep::fillModel( const QVariantList& items ) continue; } - m_model->addPackage( PackageItem( item_map ) ); + if ( item_map.contains( "appdata" ) ) + { + m_model->addPackage( PackageItem::fromAppData( CalamaresUtils::getString( item_map, "appdata" ) ) ); + } + else + { + m_model->addPackage( PackageItem( item_map ) ); + } } } diff --git a/src/modules/packagechooser/packagechooser.conf b/src/modules/packagechooser/packagechooser.conf index 7d60b50a4..f20d1da77 100644 --- a/src/modules/packagechooser/packagechooser.conf +++ b/src/modules/packagechooser/packagechooser.conf @@ -24,8 +24,11 @@ mode: required # pretty short list to avoid overwhelming the UI. This is a list # of objects, and the items are displayed in list order. # -# Each item has an id, which is used in setting # the value of -# *packagechooser_*). The following fields +# Either provide the data for an item in the list (using the keys +# below), or use existing AppData XML files as a source for the data. +# +# For data provided by the list: the item has an id, which is used in +# setting the value of *packagechooser_*). The following fields # are mandatory: # # - *id* ID for the product. The ID "" is special, and is used for @@ -33,13 +36,24 @@ mode: required # selecting none. # - *package* Package name for the product. While mandatory, this is # not actually used anywhere. -# - *name* Human-readable, but untranslated, name of the product. -# - *description* Human-readable, but untranslated, description. +# - *name* Human-readable name of the product. To provide translations, +# add a *[lang]* decoration as part of the key name, e.g. `name[nl]` +# for Dutch. The list of usable languages can be found in +# `CMakeLists.txt` or as part of the debug output of Calamares. +# - *description* Human-readable description. These can be translated +# as well. # - *screenshot* Path to a single screenshot of the product. May be # a filesystem path or a QRC path (e.g. ":/images/no-selection.png"). # # Use the empty string "" as ID / key for the "no selection" item if # you want to customize the display of that item as well. +# +# For data provided by AppData XML: the item has an *appdata* +# key which points to an AppData XML file in the local filesystem. +# This file is parsed to provide the id (from AppData id), name +# (from AppData name), description (from AppData description paragraphs +# or the summary entries), and a screenshot (the defautl screenshot +# from AppData). No package is set (but that is unused anyway). items: - id: "" package: "" @@ -58,5 +72,5 @@ items: name: GNOME description: GNU Networked Object Modeling Environment Desktop screenshot: ":/images/gnome.png" - + - appdata: ../io.calamares.calamares.appdata.xml From 6ddae94628d3c3b9cbae028306c3896727477cde Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 22:36:35 +0200 Subject: [PATCH 11/16] [packagechooser] Allow to override some of AppData - The ID and Screenshot entries might be weird in AppData (in particular, a remove URL) so put those back under the control of Calamares even when using AppData as the source of descriptions. --- .../packagechooser/PackageChooserViewStep.cpp | 2 +- src/modules/packagechooser/PackageModel.cpp | 23 ++++++++++++++++--- src/modules/packagechooser/PackageModel.h | 8 ++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/modules/packagechooser/PackageChooserViewStep.cpp b/src/modules/packagechooser/PackageChooserViewStep.cpp index fadacf8d4..6df785a06 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.cpp +++ b/src/modules/packagechooser/PackageChooserViewStep.cpp @@ -234,7 +234,7 @@ PackageChooserViewStep::fillModel( const QVariantList& items ) if ( item_map.contains( "appdata" ) ) { - m_model->addPackage( PackageItem::fromAppData( CalamaresUtils::getString( item_map, "appdata" ) ) ); + m_model->addPackage( PackageItem::fromAppData( item_map ) ); } else { diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index 29890ee9b..bddc26c7c 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -257,9 +257,15 @@ getNameAndSummary( const QDomNode& n ) #endif PackageItem -PackageItem::fromAppData( const QString& fileName ) +PackageItem::fromAppData( const QVariantMap& item_map ) { #ifdef HAVE_XML + QString fileName = CalamaresUtils::getString( item_map, "appdata" ); + if ( fileName.isEmpty() ) + { + cWarning() << "Can't load AppData without a suitable key."; + return PackageItem(); + } cDebug() << "Loading AppData XML from" << fileName; QDomDocument doc = loadAppData( fileName ); @@ -271,17 +277,28 @@ PackageItem::fromAppData( const QString& fileName ) QDomElement componentNode = doc.documentElement(); if ( !componentNode.isNull() && componentNode.tagName() == "component" ) { - QString id = getChildText( componentNode, "id" ); + // An "id" entry in the Calamares config overrides ID in the AppData + QString id = CalamaresUtils::getString( item_map, "id" ); + if ( id.isEmpty() ) + { + id = getChildText( componentNode, "id" ); + } if ( id.isEmpty() ) { return PackageItem(); } - QString screenshotPath = getScreenshotPath( componentNode ); + // A "screenshot" entry in the Calamares config overrides AppData + QString screenshotPath = CalamaresUtils::getString( item_map, "screenshot" ); + if ( screenshotPath.isEmpty() ) + { + screenshotPath = getScreenshotPath( componentNode ); + } QVariantMap map = getNameAndSummary( componentNode ); map.insert( "id", id ); map.insert( "screenshot", screenshotPath ); + return PackageItem( map ); } diff --git a/src/modules/packagechooser/PackageModel.h b/src/modules/packagechooser/PackageModel.h index 7e79b98f1..869e124f0 100644 --- a/src/modules/packagechooser/PackageModel.h +++ b/src/modules/packagechooser/PackageModel.h @@ -82,11 +82,17 @@ struct PackageItem bool isValid() const { return !name.isEmpty(); } /** @brief Loads an AppData XML file and returns a PackageItem + * + * The @p map must have a key *appdata*. That is used as the + * primary source of information, but keys *id* and *screenshotPath* + * may be used to override parts of the AppData -- so that the + * ID is under the control of Calamares, and the screenshot can be + * forced to a local path available on the installation medium. * * Requires XML support in libcalamares, if not present will * return invalid PackageItems. */ - static PackageItem fromAppData( const QString& filename ); + static PackageItem fromAppData( const QVariantMap& map ); }; using PackageList = QVector< PackageItem >; From 0b72006ffb53fa7ad31a865eb145d8ca5eea34d0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 22:41:50 +0200 Subject: [PATCH 12/16] [packagechooser] Use overrides on AppData for example --- src/modules/packagechooser/images/calamares.png | Bin 0 -> 8313 bytes src/modules/packagechooser/packagechooser.conf | 10 +++++++++- src/modules/packagechooser/packagechooser.qrc | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/modules/packagechooser/images/calamares.png diff --git a/src/modules/packagechooser/images/calamares.png b/src/modules/packagechooser/images/calamares.png new file mode 100644 index 0000000000000000000000000000000000000000..452e4450c56c10cda33dcc9c5d03753ace458862 GIT binary patch literal 8313 zcmd6NXIE3*^Y%$0gd#1J04hx>q7XU=Lg*m9b1NOBD!qt82&ggiqDZeQRRmO|6RLn9 zMGz2a2I&Zf8X(|5{$4*H;K^E9XU~;6vu9s3d(LDhv8Kkl=jeIp0RT9sucu`W03hlR z1i)d`i(O!uJM{t$($KerQ(qBqH#}9Q^Vj~7d2f!uynjDv`1ix(nX#3V-}C>?dEr>@ z9|$!h22gGzR|N60?cBbERn-W|)m>5b0xUnCtN}x_aN`)U{L+lvfVvw}8i?sbxdG5r z=RN=~*p&ie(ZUHp`0oK{lTu>?7TmQ~3%X{29gG)as^@#xk1P zMyU$VorzFC*;*i>rF!Atlge}tOT z7p4@tOPo#K-Dw{Xgw;z|CpfNB0j4IG$I36>DYFusq7C^2M!pHEtm#k$^8};a)NSHc z<3fqFg9=R&`+(32Jm1m}0#@wi885oE^Y zs-GbIh2|QjWEs`qetUrT&MgrZQ8z%qoSHB4e}8vP{W;=`S)w=NDs}B-`Fm7A;B6)x z!Ep-P-uOFj$R_tdc6u@3;h$|KY0OGl%+B~y$Fk6PToXX-E(mOQ*; z3KRjipn#phT@tg=H}k%aJ=WSz0YT9UM6T}d&|(%s`xof1Eg%aWl>#CgN>b5LXfZnh zEf1>yhf;OPdTLa(A4=rMsQ=i)stb>Zy8)`wfu!bRHbUO;Wd+w$kL|eNp27m+Jn85k zh~~{|e>dwFbd5O+?MF4xxAzyr`|L2{dq#^+H3hwC&3N!&#W8&UBvR?`=tDx1LeyT~ ziyio9H?X}XY(d6?IbuKofwv0dkEwB7UTt;kr7F#t=P!@hFyf@M(7pfspEl$dBi4J+ z_Cc@X82S0Xx`6c&1ih=1{m|>tjQr>{UvMlUYRvtS8`$68Dfr ztc2G0ELmsq;HC6jlgLDJZ=*g87BOyXbl z&7N%?P3WlBgb)=)vV{GMq6=)^g!J(Bz-;uJB&wP2#|_!&DI#qrYn05lglvEtoq*4w zbJL~>7Gyj7%4c8h16*|M46$C{*$9Q7-roW51ur0__uK2SGk|p@xiWG^F0lU#Qsw0H zTebQ%=C+s_+rK|@K>e&#ZP0jh%sQ!vU7ZzenAP;xr%fB&x@nPVMT%bx88R(@7=hBJ zI)GGbhM1*Tk{I4Of!zQpk#gabU=qVuU#dXI?tS|(C27UM&~C!cwGTcn~baLpFHIJ?at$fHZ00loS`X*@du6HP6Z1dXzxbkS(P8i~vMW z43mWxA^PcgDix>te*EnqeFI12TBZBvGy7OI9knJZ@Cd>)4(-NVuVU2M46&a8bK`1P zumdr9rj3~ba;gX#!1pXmcDxcg%76`p! za&w0dqo62>VSOeJ<^tnJ<7_`e-|=IH54+XCEClKn_xR_&RLdvmyQ>&peTsUb`YH^d zF>O8tgQ$Upc_l%mCC_kb7F!OK*(CLDF}6ZD|4aPSA~?+ zN6p5e_Uu~f92$`E0{mc4CW_(J-`ZiUpRnE`0V%tePh&ey_%e zTDv{fEwH1xpiex)efQG%Fu(b$w=o>R>p!ixRw1u-Wh5T@!G=2x_O=Yq> zjj9x)uQJ^~uS@hRFkvQUaCscRwV@htMPdc|p)+$~ zMk1s`%Zne3J8I;=??JhNQI3yvqE%~ed!LaXUNPalbNtV^)+7qhtk^{xvPGRlL|jdt zx$vil$|gRa+fr%g;x&9XLF>UbF@yQ{kAG+z(o|`uVt3kSjGBG(y5ZB;n6?I>o|-Jh ze)Mr-2Gz8#2VXVxDe?})jIsZ67N@BkkTukQ0<_*OwPGiiW!a*1*X%XzfldYbipFYe zyJV5vuXI+D3}*`&6bHZy`o#f=?zdHA?||V?jhs(3gK8dJgA68_fD)HzZh@$hXL_&BCRNxAFO3D=%pc&S3bTYUiYC#$Qm4!=kKN0%+^&zvqoF>IhOLCy!q*ib(Hxv-gVagp#D2OQqX_$|Ktrc$Hld(nBhT-MAR~GM55%wkT2RVrm7e3-9zggJS2S zs3Dn3wYb$w&F2nkl6I%%##}DY_(c;-2%3uRFNZ`IXjda45o zQ_MiSDd{T_&BV4Kx?M8%>*$1bpz7A4Z8hchA%mv2=aILK$OcGWIqdp#8d~V6c+*?W zNuL8B$<&ACGX{4943fdqta$v5?2>3HSgZA}s;Ik*aSR#61L0?jsi+tUR60~}6<&UY z=cAIz_PC;=Z)rC%Rtgc6*8MLSbed-DW6JEEK$qFSJX_xw`S5kU+LM#5^Rp$rtk~I&9x9%Hf`C zKHO;5H{oFiv6PoQ)8mg1!+;X0V_j!@M&J9xOm1bv^Ne4>ELm7F`gWxTXT8`p`-L&F z4@}rT#)OWD1w zYAdSgOkfNF;Rie02bCkZW}`o-+XFG4($OEDo=SgU!SgR%-O?WuEHK7n7@Y!m6e)^R zl_t>z54i6x%N5>&?$M(f7?Ih)Cu>NK1kHQ|O-+D83NQZSpuXEW%HK_L%Ets`Q4KJ> zM|*LWZJ;r5+mDb zzjB}!pTTrNtM1ksIVeGFs=-Sp)ZxB>yzlgR{XNzuP>qlY_Qu}1g1 zfuRnCpEvu_=BS*X68oGH!QOa?;c)VHTEQ!C_W2dgoMfQGRZ}R9*yJdYT#S+G}_j~N{uZ66YV&j3^7&NY9@>{D45>iICh^vBslQ&mzm zHm%ho|J|vY{5A#Gw$~Avhj%`^R?sn`teT^QKruqpP6-7I`;!jmD0W?4LgdrK7UltA zVek!;nlbFINc&dPHFgpNz4b?E;V=@k`d;mY?3B;LB9%W&3q8z0uInagyO=z%O%wGN z*`d_iMWASl5^1qLJv0<@2d)3|U>17#<=k0mRH!b$(uS4iJb~lxPCEMCp+?pN?j~pK zy=Lk0T^*X72Q=$Z82Yjo8aMoYt~v}}n~(1N?>1Gi-28FKexP<@Shn(JqCzp8s5mrR zD^%lY0HTc{n_gd3lFglxr>z^(*LEiT! z={Pcj{u&w?yUfqErskfE_ghSzbo+q^)G{U7%Qa2&Tf|e|D0h}=hA#}1S$!T&7l2M# z9AaC4?s`UXdG_f4;yaO8cJ~kx*BtTpT^MydrN_0T>h*0#Epr>17}2EFvpudARXZ^b zi+>2_S39MTOP+_cIwT*%*al7N;QL&Ei9UJzo#c3C7ZTYZGcPB;%@|Bp3jV_{EYii@ zwbO}WR5|+P2R_2kD?Tky+nm}pb--L5r{8f2{(Gtzvh%7@=FRZD%sSuF8Xt}P>?DWI z1;ojAZzn}%Xs{ru#eqWkiHklR*5Y#RZy_CTWaek@ohl!zI+l=IM@pG@-s_^sL`}sT zK}^CgjL>FZW$wLJA)S`RJIMr+x$Ym$FuR8Oa=v`&isZhS)TGyjl95Tw%E~H>2LTJY ziwn_^hUKP>$S;%gYP8ECHCz{WWP02F+h0}FvVT1Oay(9P|4)>2)V*`<$er+WjpwiN z9^`aM5*}Apw+>BmgB)o=-)kXGk#BE^r1i_^zEhZfGvPh5&oXhzEg`TmA@ zVw$wITj#G8XnXqxtUcV?__g_E1RRU#O+HbEE%X(CS?OFFq-o?m0Yi`?1fpAa ziw5w#b@0NSwr6wmDrNi)!5%x-OBqbkV~HzreVlQZ6mPFkuDl=_&%n^OTpE3{D)LSH z?~Wjw?#1iK+^4?&Kmz2OH%CI&@S4!^%Uf0LCSQ(!(BSWrm|3q~sfJ8gia*_FVamjq zR#}^gvKNYeICn=4iDR9Y00Kw1{B*PotfWfMvZ>!DEGttb%8eixKX&3q5Qsby_*Yse zjmA~92pG!g_N$W|W5;^Gj*BP)F1tRYBlono^HZIzZU*mi+>RKC*W+BGC|>DnoI6s= zrM>6xKVHG{D=>VWX|Mg=oq>f?cPcrkiTrI>U#+yvp#mD@AlpL=#GOH&dTG9{JN`kT z{22qObiw^Q>EesZFw^<_SxU@?Zb_5h>HwZ?N@&a_%u(j8Djip_eW57*(5?3qlPaf? z@=ng|pI%fJoIq8d#K!uevJr4KO)dNh8?BQ%seQU9l_s4=r3Ccv}wwwcDK^VDsh7)TIbnpFA z&aMWGzdd%l+jM)k`HP1%^?VAp+mZX)Tr6}pIzlx$X!r+fh!g;H2?(=5HH@C1I0;SW* z>5FfqtPJDz!^vjD)W_<0*bGv~>f`Ho4`^?0RDWpWdcX1G{60d?^INUGTFsdA=`u{W z(Gq8-K)IJ3d1hA=WaG)a=GohmVF!C6))uAqr0efr?`Ac?b#^GFvMlwxxayPRKQ{qv zYiL}+dxOt|fpNx)Tmk_zR`%I8E!C@d!G{$mlt zjy<_3kmy97eTA;su%J;J?A$(ND#Xhll75pLrU3R#Z#`9zl}<2_BnZwtxaab1{hQ*+ zOc3M<)6tz`sJ`?gDWAQoyvkfFIV;F%le(+@T4A4m35bqW!!Qy+*_-RkrM}0?{#E>m*kF;f zi4LpGNKkoGLq`WY&AC|C)PbcM-!9mBCCaJMHQe+X_(I9}z+Z8_R5GPxVdO7Qgjqfa?7b^1l*TKYEJ%=$(VvaeOc>?&6MBk`HS zES&?As$`LIil2Y0?4DF#=B?tc41>XHoX3P)0`^3`RaH3}IB=EKjhxC7aLjq5(_kzs zwy@~|Z3(S{3z@7c3cBhbRuCd!+;);vmR$8EpG;$@e(y(!t>`2wI8ZHOb_E0=yVe-g z@N2wn@O)lIJIXC3^F&A%LwjI%FWv`W+VE+K4J*fpmR* zKisioWA`Mw;}2|DQwC_4jkYKiHp^7Oatxm;=*-P9)XKi%U4IgAHTNOHc>9=T{SktN z!SI~~CfXU9nU$~gv!}r2sDoAtb!u*MG_EuGb5EPEr&DPgYE#$OEjj6vYX|^wJ%UUs z1U|_N*YP)*lZ#!Q{?}ptZi9;3RM8hcHpu-;YCX}o;Qh@<&d}JXvy8Vdy##fzE`(J5 z$P9TVsZst=*(=hM<|g$tF5vWz?ac8*taS}9Uo3%RNXfm#!}RTQ6e<$#TCIvJvKV%C zT1K_E+RRw>DinX%a=Gf;2uhdZx|J>(dtHMx(EBN|>Fr{jP4YsbLK5*;?4l@Ve15NY z1HK+4puU&gRTUQ=59DvD`tGUvxAaB^~* zUg0z=e{Lp`QB;2AgB_Er>mu?3Z~T`-d^Z4~CqoR4sEh@nK2_m_d9p4!Rhp%eA3h37 z`ri1T<;^?Fw_>b@jc=$&=(9zA`V*pC_}2JMxouM+#BZ^M)(gL2ou>}T1 z+U_(vcbbBaW_kju{DOv>-Zg@W3a_&Cae2wlhAxu~yb|Q{#4j>f&p0vKs!ByG4HjEU z{6~Cxuj%9aX`Rni!&3emNSX3FjAFxOOsQ&*(vwp8aYWdTUyHDWmwBsjQU0?yLNhol zw!+J{EkeoN-Myn~*0?9{vBK<;cICp?JU;WLT|bMDC31?9;iH1Tl?(frYqlvZVzVvI zaC!$Wy0@%lzM- z{RxyA?Pvbq)K&S{_JEL9*jt%fAhJuu0*FbN0TyYIR-Tz{t|urG!W)4$-^#C0`K`<<4A8a+j;hh z8wB8jSN!OmZ#RMIm`_@f7r38vB5(Gj*{cCOU6|&%S8p@IEe)pT?;E|U;qxr>Si5NA z9L0#&=0fX?$tFnNhLv0ijbis)XR@UWMP7*~h;3$+?oZ=R1NCy>)y&8L{K?A5bnbiD z=0|5jnOQ~+ft3A$?j%o({=`+|)qB99EE`u_8!$LLd^;5Y3_c9ORW?H)i7&^OYmIFO za{o*)o#eqJblxgukEJP1&tKE*>H=h%A)&fAD-0lsuA+f?{gYbo*^9TIf6bMMKH}#2 zoy?qC1+k?!vXHtiANS-a94PLfE4m$a^5V$L;}SamJ#Ks@<43)1kdyr9lAud&`SZ*; z9IOLrDv_T1z_WQmW9I0~WXgIuX-IN?`bFb&_ejn@!yCP+wuvCwXBKh^2}wl_HBQNN znJAPdThr>U!Hw~x6Uu?CZPN+GKuQq>r*UzrsU7x9X6=)aDfYd-wtJg_$=2c&^1X7U zfptIvn^59~=Kvz_opZfl`p6vn|;kv(0Mi|%Dw|AUResXn39Cqe3@iFD1Brb26B_N>Ka*Kx!EJg z|AZW>U`bB`nNH2qK51#3bbM`|x=wSgQcP^qM74Gs+()H7x=5h8z&lJ+Z1XVBPL&x| zXkLEnN@images/no-selection.png images/kde.png images/gnome.png + images/calamares.png From 5d4c3ea92c028b7e102cc81dc31ba33e271863a4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 22:46:36 +0200 Subject: [PATCH 13/16] [packagechooser] Don't give a default pixmap --- src/modules/packagechooser/PackageModel.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index bddc26c7c..59c6973ba 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -92,10 +92,6 @@ PackageItem::PackageItem::PackageItem( const QVariantMap& item_map ) { description = QObject::tr( "No description provided." ); } - if ( screenshot.isNull() ) - { - screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); - } } #ifdef HAVE_XML From a4362dac654b154e837d4035027c6980ce47101f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 22:47:37 +0200 Subject: [PATCH 14/16] [packagechooser] Change tests for modified API --- src/modules/packagechooser/Tests.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/modules/packagechooser/Tests.cpp b/src/modules/packagechooser/Tests.cpp index 5dec8a288..15b5792bb 100644 --- a/src/modules/packagechooser/Tests.cpp +++ b/src/modules/packagechooser/Tests.cpp @@ -49,7 +49,10 @@ PackageChooserTests::testAppData() QString appdataName( "../io.calamares.calamares.appdata.xml" ); QVERIFY( QFile::exists( appdataName ) ); - PackageItem p = PackageItem::fromAppData( appdataName ); + QVariantMap m; + m.insert( "appdata", appdataName ); + + PackageItem p = PackageItem::fromAppData( m ); #ifdef HAVE_XML QVERIFY( p.isValid() ); QCOMPARE( p.id, "io.calamares.calamares.desktop" ); @@ -59,6 +62,15 @@ PackageChooserTests::testAppData() // .. but en_GB doesn't have an entry in description, so uses QCOMPARE( p.description.get( QLocale( "en_GB" ) ), "Calamares Linux Installer" ); QCOMPARE( p.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); + QVERIFY( p.screenshot.isNull() ); + + m.insert( "id", "calamares" ); + m.insert( "screenshot", ":/images/calamares.png" ); + PackageItem p_self = PackageItem::fromAppData( m ); + QVERIFY( p.isValid() ); + QCOMPARE( p.id, "calamares" ); + QCOMPARE( p.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); + QVERIFY( !p.screenshot.isNull() ); #else QVERIFY( !p.isValid() ); #endif From 194a562727e347a32b246613e05836cc405264e4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 22:51:14 +0200 Subject: [PATCH 15/16] [packagechooser] Expand tests with override features --- src/modules/packagechooser/Tests.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/modules/packagechooser/Tests.cpp b/src/modules/packagechooser/Tests.cpp index 15b5792bb..3e7961b92 100644 --- a/src/modules/packagechooser/Tests.cpp +++ b/src/modules/packagechooser/Tests.cpp @@ -52,26 +52,26 @@ PackageChooserTests::testAppData() QVariantMap m; m.insert( "appdata", appdataName ); - PackageItem p = PackageItem::fromAppData( m ); + PackageItem p1 = PackageItem::fromAppData( m ); #ifdef HAVE_XML - QVERIFY( p.isValid() ); - QCOMPARE( p.id, "io.calamares.calamares.desktop" ); - QCOMPARE( p.name.get(), "Calamares" ); + QVERIFY( p1.isValid() ); + QCOMPARE( p1.id, "io.calamares.calamares.desktop" ); + QCOMPARE( p1.name.get(), "Calamares" ); // The entry has precedence - QCOMPARE( p.description.get(), "Calamares is an installer program for Linux distributions." ); + QCOMPARE( p1.description.get(), "Calamares is an installer program for Linux distributions." ); // .. but en_GB doesn't have an entry in description, so uses - QCOMPARE( p.description.get( QLocale( "en_GB" ) ), "Calamares Linux Installer" ); - QCOMPARE( p.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); - QVERIFY( p.screenshot.isNull() ); + QCOMPARE( p1.description.get( QLocale( "en_GB" ) ), "Calamares Linux Installer" ); + QCOMPARE( p1.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); + QVERIFY( p1.screenshot.isNull() ); m.insert( "id", "calamares" ); m.insert( "screenshot", ":/images/calamares.png" ); - PackageItem p_self = PackageItem::fromAppData( m ); - QVERIFY( p.isValid() ); - QCOMPARE( p.id, "calamares" ); - QCOMPARE( p.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); - QVERIFY( !p.screenshot.isNull() ); + PackageItem p2= PackageItem::fromAppData( m ); + QVERIFY( p2.isValid() ); + QCOMPARE( p2.id, "calamares" ); + QCOMPARE( p2.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." ); + QVERIFY( !p2.screenshot.isNull() ); #else - QVERIFY( !p.isValid() ); + QVERIFY( !p1.isValid() ); #endif } From 1b29ca5697515e0b369d87539d990620c70cdfe5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 6 Aug 2019 23:43:29 +0200 Subject: [PATCH 16/16] Changes: document packagechooser and others --- CHANGES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index c84f245b0..d41d4b8fa 100644 --- a/CHANGES +++ b/CHANGES @@ -18,12 +18,25 @@ This release contains contributions from (alphabetically by first name): number of jobs. (Thanks to Bill Auger) - Preliminary work has been added to post the installation log to a pastebin for bug reporting. (Thanks to Bill Auger) + - Support for translated human-readable strings in Calamares + config files has been added. This is used only in the *packagechooser* + module (see below) but will expand to those modules that need + user-visible strings from the configuration file (existing + solutions need either gettext or Qt support). + - Esperanto is now available when Qt version 5.12.2 or later is used. ## Modules ## - *fstab* A new configuration key *efiMountOptions* has been added, to allow setting filesystem options specifically for the EFI partition. (Thanks to apt-ghetto) + - *packagechooser* is a new module for low-density package choices, + e.g. for selecting a default desktop environment, or adding some + proprietary drivers, or chosing browsers of office suites. It presents + **one** collection of items -- at most ten or so, because of the UI -- + and the user can select zero or more of them. The behavior is + configurable, and package information can be set through the Calamares + configuration file or by reading AppData files for the packages. #426 # 3.2.11 (2019-07-06) #