Merge branch 'master' of https://github.com/calamares/calamares into development
This commit is contained in:
commit
ac5c2a041d
13
CHANGES
13
CHANGES
@ -18,12 +18,25 @@ This release contains contributions from (alphabetically by first name):
|
|||||||
number of jobs. (Thanks to Bill Auger)
|
number of jobs. (Thanks to Bill Auger)
|
||||||
- Preliminary work has been added to post the installation log to a
|
- Preliminary work has been added to post the installation log to a
|
||||||
pastebin for bug reporting. (Thanks to Bill Auger)
|
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 ##
|
## Modules ##
|
||||||
|
|
||||||
- *fstab* A new configuration key *efiMountOptions* has been added, to
|
- *fstab* A new configuration key *efiMountOptions* has been added, to
|
||||||
allow setting filesystem options specifically for the EFI partition.
|
allow setting filesystem options specifically for the EFI partition.
|
||||||
(Thanks to apt-ghetto)
|
(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) #
|
# 3.2.11 (2019-07-06) #
|
||||||
|
29
io.calamares.calamares.appdata.xml
Normal file
29
io.calamares.calamares.appdata.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<component type="desktop">
|
||||||
|
<id>io.calamares.calamares.desktop</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>GPL-3.0+</project_license>
|
||||||
|
<name>Calamares</name>
|
||||||
|
<name xml:lang="da">Calamares</name>
|
||||||
|
<name xml:lang="en-GB">Calamares</name>
|
||||||
|
<name xml:lang="nl">Calamares</name>
|
||||||
|
<summary>Calamares Linux Installer</summary>
|
||||||
|
<summary xml:lang="en-GB">Calamares Linux Installer</summary>
|
||||||
|
<summary xml:lang="nl">Linux Installatieprogramma Calamares</summary>
|
||||||
|
<description>
|
||||||
|
<p>Calamares is an installer program for Linux distributions.</p>
|
||||||
|
<p xml:lang="nl">Calamares is een installatieprogramma voor Linux distributies.</p>
|
||||||
|
</description>
|
||||||
|
<url type="homepage">https://calamares.io</url>
|
||||||
|
<url type="bugtracker">https://https://github.com/calamares/calamares/issues/</url>
|
||||||
|
<url type="help">https://github.com/calamares/calamares/wiki</url>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<caption>Calamares Welcome</caption>
|
||||||
|
<image>https://calamares.io/images/cal_640.png</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<provides>
|
||||||
|
<binary>calamares</binary>
|
||||||
|
</provides>
|
||||||
|
</component>
|
@ -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
|
void
|
||||||
LocaleTests::testTranslatableLanguages()
|
LocaleTests::testTranslatableLanguages()
|
||||||
{
|
{
|
||||||
@ -108,12 +112,19 @@ LocaleTests::testTranslatableLanguages()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Test strings with no translations
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
LocaleTests::testTranslatableConfig1()
|
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
|
QCOMPARE( QLocale().name(), "C" ); // Otherwise plain get() is dubious
|
||||||
CalamaresUtils::Locale::TranslatedString ts1( "Hello" );
|
CalamaresUtils::Locale::TranslatedString ts1( "Hello" );
|
||||||
QCOMPARE( ts1.count(), 1 );
|
QCOMPARE( ts1.count(), 1 );
|
||||||
|
QVERIFY( !ts1.isEmpty() );
|
||||||
|
|
||||||
QCOMPARE( ts1.get(), "Hello" );
|
QCOMPARE( ts1.get(), "Hello" );
|
||||||
QCOMPARE( ts1.get( QLocale( "nl" ) ), "Hello" );
|
QCOMPARE( ts1.get( QLocale( "nl" ) ), "Hello" );
|
||||||
@ -122,11 +133,14 @@ LocaleTests::testTranslatableConfig1()
|
|||||||
map.insert( "description", "description (no language)" );
|
map.insert( "description", "description (no language)" );
|
||||||
CalamaresUtils::Locale::TranslatedString ts2( map, "description" );
|
CalamaresUtils::Locale::TranslatedString ts2( map, "description" );
|
||||||
QCOMPARE( ts2.count(), 1 );
|
QCOMPARE( ts2.count(), 1 );
|
||||||
|
QVERIFY( !ts2.isEmpty() );
|
||||||
|
|
||||||
QCOMPARE( ts2.get(), "description (no language)" );
|
QCOMPARE( ts2.get(), "description (no language)" );
|
||||||
QCOMPARE( ts2.get( QLocale( "nl" ) ), "description (no language)" );
|
QCOMPARE( ts2.get( QLocale( "nl" ) ), "description (no language)" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @bref Test strings with translations.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
LocaleTests::testTranslatableConfig2()
|
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" );
|
CalamaresUtils::Locale::TranslatedString ts1( map, "description" );
|
||||||
// The +1 is because "" is always also inserted
|
// The +1 is because "" is always also inserted
|
||||||
QCOMPARE( ts1.count(), someLanguages().count() + 1 );
|
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)" );
|
QCOMPARE( ts1.get( QLocale( "nl" ) ), "description (language nl)" );
|
||||||
for ( const auto& language : someLanguages() )
|
for ( const auto& language : someLanguages() )
|
||||||
{
|
{
|
||||||
@ -167,4 +192,10 @@ LocaleTests::testTranslatableConfig2()
|
|||||||
CalamaresUtils::Locale::TranslatedString ts2( map, "name" );
|
CalamaresUtils::Locale::TranslatedString ts2( map, "name" );
|
||||||
// We skipped dutch this time
|
// We skipped dutch this time
|
||||||
QCOMPARE( ts2.count(), someLanguages().count() );
|
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
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,6 @@ TranslatedString::TranslatedString( const QVariantMap& map, const QString& key )
|
|||||||
{
|
{
|
||||||
// Get the un-decorated value for the key
|
// Get the un-decorated value for the key
|
||||||
QString value = CalamaresUtils::getString( map, key );
|
QString value = CalamaresUtils::getString( map, key );
|
||||||
if ( value.isEmpty() )
|
|
||||||
{
|
|
||||||
value = key;
|
|
||||||
}
|
|
||||||
m_strings[ QString() ] = value;
|
m_strings[ QString() ] = value;
|
||||||
|
|
||||||
for ( auto it = map.constKeyValueBegin(); it != map.constKeyValueEnd(); ++it )
|
for ( auto it = map.constKeyValueBegin(); it != map.constKeyValueEnd(); ++it )
|
||||||
|
@ -46,9 +46,23 @@ public:
|
|||||||
TranslatedString( const QString& string );
|
TranslatedString( const QString& string );
|
||||||
/// @brief Empty string
|
/// @brief Empty string
|
||||||
TranslatedString()
|
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(); }
|
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
|
/// @brief Gets the string in the current locale
|
||||||
QString get() const;
|
QString get() const;
|
||||||
|
@ -1,4 +1,15 @@
|
|||||||
find_package( Qt5 COMPONENTS Core Gui Widgets REQUIRED )
|
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
|
calamares_add_plugin( packagechooser
|
||||||
TYPE viewmodule
|
TYPE viewmodule
|
||||||
@ -13,6 +24,7 @@ calamares_add_plugin( packagechooser
|
|||||||
page_package.ui
|
page_package.ui
|
||||||
LINK_PRIVATE_LIBRARIES
|
LINK_PRIVATE_LIBRARIES
|
||||||
calamaresui
|
calamaresui
|
||||||
|
${_extra_libraries}
|
||||||
SHARED_LIB
|
SHARED_LIB
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,8 +35,11 @@ if( ECM_FOUND AND BUILD_TESTING )
|
|||||||
packagechoosertest
|
packagechoosertest
|
||||||
LINK_LIBRARIES
|
LINK_LIBRARIES
|
||||||
${CALAMARES_LIBRARIES}
|
${CALAMARES_LIBRARIES}
|
||||||
|
calamares_viewmodule_packagechooser
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::Test
|
Qt5::Test
|
||||||
|
Qt5::Gui
|
||||||
|
${_extra_libraries}
|
||||||
)
|
)
|
||||||
calamares_automoc( packagechoosertest)
|
calamares_automoc( packagechoosertest)
|
||||||
endif()
|
endif()
|
||||||
|
@ -232,31 +232,14 @@ PackageChooserViewStep::fillModel( const QVariantList& items )
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString id = CalamaresUtils::getString( item_map, "id" );
|
if ( item_map.contains( "appdata" ) )
|
||||||
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" );
|
m_model->addPackage( PackageItem::fromAppData( item_map ) );
|
||||||
}
|
}
|
||||||
else if ( name.isEmpty() )
|
else
|
||||||
{
|
{
|
||||||
cWarning() << "PackageChooser item" << id << "has an empty name.";
|
m_model->addPackage( PackageItem( item_map ) );
|
||||||
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 } );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,13 @@
|
|||||||
#include "PackageModel.h"
|
#include "PackageModel.h"
|
||||||
|
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/Variant.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_XML
|
||||||
|
#include <QDomDocument>
|
||||||
|
#include <QDomNodeList>
|
||||||
|
#include <QFile>
|
||||||
|
#endif
|
||||||
|
|
||||||
const NamedEnumTable< PackageChooserMode >&
|
const NamedEnumTable< PackageChooserMode >&
|
||||||
roleNames()
|
roleNames()
|
||||||
@ -40,13 +47,6 @@ roleNames()
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageItem
|
|
||||||
PackageItem::fromAppStream( const QString& filename )
|
|
||||||
{
|
|
||||||
// TODO: implement this
|
|
||||||
return PackageItem {};
|
|
||||||
}
|
|
||||||
|
|
||||||
PackageItem::PackageItem() {}
|
PackageItem::PackageItem() {}
|
||||||
|
|
||||||
PackageItem::PackageItem( const QString& a_id,
|
PackageItem::PackageItem( const QString& a_id,
|
||||||
@ -73,6 +73,239 @@ 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." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_XML
|
||||||
|
/** @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 );
|
||||||
|
if ( !file.open( QIODevice::ReadOnly ) )
|
||||||
|
{
|
||||||
|
return QDomDocument();
|
||||||
|
}
|
||||||
|
QDomDocument doc( "AppData" );
|
||||||
|
if ( !doc.setContent( &file ) )
|
||||||
|
{
|
||||||
|
file.close();
|
||||||
|
return QDomDocument();
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @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 <screenshots> element contains zero or more <screenshot>
|
||||||
|
* elements, which can have a *type* associated with them.
|
||||||
|
* Scan the screenshot elements, return the <image> 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 <name> and <description> elements
|
||||||
|
*
|
||||||
|
* Builds up a map of the <name> elements (which may have a *lang*
|
||||||
|
* attribute to indicate translations and paragraphs of the
|
||||||
|
* <description> element (also with lang). Uses the <summary>
|
||||||
|
* 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
|
||||||
|
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 );
|
||||||
|
if ( doc.isNull() )
|
||||||
|
{
|
||||||
|
return PackageItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDomElement componentNode = doc.documentElement();
|
||||||
|
if ( !componentNode.isNull() && componentNode.tagName() == "component" )
|
||||||
|
{
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return PackageItem();
|
||||||
|
#else
|
||||||
|
cWarning() << "Loading AppData XML is not supported.";
|
||||||
|
|
||||||
|
return PackageItem();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PackageListModel::PackageListModel( QObject* parent )
|
PackageListModel::PackageListModel( QObject* parent )
|
||||||
: QAbstractListModel( parent )
|
: QAbstractListModel( parent )
|
||||||
@ -90,10 +323,14 @@ PackageListModel::~PackageListModel() {}
|
|||||||
void
|
void
|
||||||
PackageListModel::addPackage( PackageItem&& p )
|
PackageListModel::addPackage( PackageItem&& p )
|
||||||
{
|
{
|
||||||
|
// Only add valid packages
|
||||||
|
if ( p.isValid() )
|
||||||
|
{
|
||||||
int c = m_packages.count();
|
int c = m_packages.count();
|
||||||
beginInsertRows( QModelIndex(), c, c );
|
beginInsertRows( QModelIndex(), c, c );
|
||||||
m_packages.append( p );
|
m_packages.append( p );
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -40,11 +40,10 @@ const NamedEnumTable< PackageChooserMode >& roleNames();
|
|||||||
struct PackageItem
|
struct PackageItem
|
||||||
{
|
{
|
||||||
QString id;
|
QString id;
|
||||||
// TODO: may need more than one
|
// FIXME: unused
|
||||||
QString package;
|
QString package;
|
||||||
CalamaresUtils::Locale::TranslatedString name;
|
CalamaresUtils::Locale::TranslatedString name;
|
||||||
CalamaresUtils::Locale::TranslatedString description;
|
CalamaresUtils::Locale::TranslatedString description;
|
||||||
// TODO: may be more than one
|
|
||||||
QPixmap screenshot;
|
QPixmap screenshot;
|
||||||
|
|
||||||
/// @brief Create blank PackageItem
|
/// @brief Create blank PackageItem
|
||||||
@ -56,14 +55,44 @@ struct PackageItem
|
|||||||
*/
|
*/
|
||||||
PackageItem( const QString& id, const QString& package, const QString& name, const QString& description );
|
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,
|
PackageItem( const QString& id,
|
||||||
const QString& package,
|
const QString& package,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& description,
|
const QString& description,
|
||||||
const QString& screenshotPath );
|
const QString& screenshotPath );
|
||||||
|
|
||||||
// TODO: implement this
|
/** @brief Creates a PackageItem from a QVariantMap
|
||||||
PackageItem fromAppStream( const QString& filename );
|
*
|
||||||
|
* 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 );
|
||||||
|
|
||||||
|
/** @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
|
||||||
|
*
|
||||||
|
* 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 QVariantMap& map );
|
||||||
};
|
};
|
||||||
|
|
||||||
using PackageList = QVector< PackageItem >;
|
using PackageList = QVector< PackageItem >;
|
||||||
@ -75,6 +104,10 @@ public:
|
|||||||
PackageListModel( QObject* parent );
|
PackageListModel( QObject* parent );
|
||||||
virtual ~PackageListModel() override;
|
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 );
|
void addPackage( PackageItem&& p );
|
||||||
|
|
||||||
int rowCount( const QModelIndex& index ) const override;
|
int rowCount( const QModelIndex& index ) const override;
|
||||||
|
@ -18,9 +18,13 @@
|
|||||||
|
|
||||||
#include "Tests.h"
|
#include "Tests.h"
|
||||||
|
|
||||||
|
#include "PackageModel.h"
|
||||||
|
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN( PackageChooserTests )
|
QTEST_MAIN( PackageChooserTests )
|
||||||
|
|
||||||
PackageChooserTests::PackageChooserTests() {}
|
PackageChooserTests::PackageChooserTests() {}
|
||||||
|
|
||||||
@ -29,6 +33,7 @@ PackageChooserTests::~PackageChooserTests() {}
|
|||||||
void
|
void
|
||||||
PackageChooserTests::initTestCase()
|
PackageChooserTests::initTestCase()
|
||||||
{
|
{
|
||||||
|
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -36,3 +41,37 @@ PackageChooserTests::testBogus()
|
|||||||
{
|
{
|
||||||
QVERIFY( true );
|
QVERIFY( true );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PackageChooserTests::testAppData()
|
||||||
|
{
|
||||||
|
// Path from the build-dir
|
||||||
|
QString appdataName( "../io.calamares.calamares.appdata.xml" );
|
||||||
|
QVERIFY( QFile::exists( appdataName ) );
|
||||||
|
|
||||||
|
QVariantMap m;
|
||||||
|
m.insert( "appdata", appdataName );
|
||||||
|
|
||||||
|
PackageItem p1 = PackageItem::fromAppData( m );
|
||||||
|
#ifdef HAVE_XML
|
||||||
|
QVERIFY( p1.isValid() );
|
||||||
|
QCOMPARE( p1.id, "io.calamares.calamares.desktop" );
|
||||||
|
QCOMPARE( p1.name.get(), "Calamares" );
|
||||||
|
// The <description> entry has precedence
|
||||||
|
QCOMPARE( p1.description.get(), "Calamares is an installer program for Linux distributions." );
|
||||||
|
// .. but en_GB doesn't have an entry in description, so uses <summary>
|
||||||
|
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 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( !p1.isValid() );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void testBogus();
|
void testBogus();
|
||||||
|
void testAppData();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
BIN
src/modules/packagechooser/images/calamares.png
Normal file
BIN
src/modules/packagechooser/images/calamares.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
@ -24,8 +24,11 @@ mode: required
|
|||||||
# pretty short list to avoid overwhelming the UI. This is a list
|
# pretty short list to avoid overwhelming the UI. This is a list
|
||||||
# of objects, and the items are displayed in list order.
|
# of objects, and the items are displayed in list order.
|
||||||
#
|
#
|
||||||
# Each item has an id, which is used in setting # the value of
|
# Either provide the data for an item in the list (using the keys
|
||||||
# *packagechooser_<module-id>*). The following fields
|
# 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_<module-id>*). The following fields
|
||||||
# are mandatory:
|
# are mandatory:
|
||||||
#
|
#
|
||||||
# - *id* ID for the product. The ID "" is special, and is used for
|
# - *id* ID for the product. The ID "" is special, and is used for
|
||||||
@ -33,13 +36,30 @@ mode: required
|
|||||||
# selecting none.
|
# selecting none.
|
||||||
# - *package* Package name for the product. While mandatory, this is
|
# - *package* Package name for the product. While mandatory, this is
|
||||||
# not actually used anywhere.
|
# not actually used anywhere.
|
||||||
# - *name* Human-readable, but untranslated, name of the product.
|
# - *name* Human-readable name of the product. To provide translations,
|
||||||
# - *description* Human-readable, but untranslated, description.
|
# 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
|
# - *screenshot* Path to a single screenshot of the product. May be
|
||||||
# a filesystem path or a QRC path (e.g. ":/images/no-selection.png").
|
# 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
|
# Use the empty string "" as ID / key for the "no selection" item if
|
||||||
# you want to customize the display of that item as well.
|
# 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).
|
||||||
|
#
|
||||||
|
# AppData may contain IDs that are not useful inside Calamares,
|
||||||
|
# and the screenshot URL may be remote -- a remote URL will not
|
||||||
|
# be loaded and the screenshot will be missing. An item with *appdata*
|
||||||
|
# **may** specify an ID or screenshot path, as above. This will override
|
||||||
|
# the settings from AppData.
|
||||||
items:
|
items:
|
||||||
- id: ""
|
- id: ""
|
||||||
package: ""
|
package: ""
|
||||||
@ -58,5 +78,7 @@ items:
|
|||||||
name: GNOME
|
name: GNOME
|
||||||
description: GNU Networked Object Modeling Environment Desktop
|
description: GNU Networked Object Modeling Environment Desktop
|
||||||
screenshot: ":/images/gnome.png"
|
screenshot: ":/images/gnome.png"
|
||||||
|
- id: calamares
|
||||||
|
appdata: ../io.calamares.calamares.appdata.xml
|
||||||
|
screenshot: ":/images/calamares.png"
|
||||||
|
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
<file>images/no-selection.png</file>
|
<file>images/no-selection.png</file>
|
||||||
<file>images/kde.png</file>
|
<file>images/kde.png</file>
|
||||||
<file>images/gnome.png</file>
|
<file>images/gnome.png</file>
|
||||||
|
<file>images/calamares.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
Loading…
Reference in New Issue
Block a user