Merge branch 'issue-1550' into calamares

FIXES #1550
This commit is contained in:
Adriaan de Groot 2021-04-23 22:37:12 +02:00
commit bac1108781
10 changed files with 482 additions and 239 deletions

View File

@ -174,6 +174,22 @@ struct NamedEnumTable
return table.begin()->second; return table.begin()->second;
} }
/** @brief Find a name @p s in the table.
*
* Searches case-insensitively.
*
* If the name @p s is not found, the value @p d is returned as
* a default. Otherwise the value corresponding to @p s is returned.
* This is a shortcut over find() using a bool to distinguish
* successful and unsuccesful lookups.
*/
enum_t find( const string_t& s, enum_t d ) const
{
bool ok = false;
enum_t e = find( s, ok );
return ok ? e : d;
}
/** @brief Find a value @p s in the table and return its name. /** @brief Find a value @p s in the table and return its name.
* *
* If @p s is an enum value in the table, return the corresponding * If @p s is an enum value in the table, return the corresponding

View File

@ -45,6 +45,7 @@ calamares_add_plugin( packagechooser
TYPE viewmodule TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES SOURCES
Config.cpp
PackageChooserPage.cpp PackageChooserPage.cpp
PackageChooserViewStep.cpp PackageChooserViewStep.cpp
PackageModel.cpp PackageModel.cpp

View File

@ -0,0 +1,224 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "Config.h"
#ifdef HAVE_XML
#include "ItemAppData.h"
#endif
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "packages/Globals.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
const NamedEnumTable< PackageChooserMode >&
packageChooserModeNames()
{
static const NamedEnumTable< PackageChooserMode > names {
{ "optional", PackageChooserMode::Optional },
{ "required", PackageChooserMode::Required },
{ "optionalmultiple", PackageChooserMode::OptionalMultiple },
{ "requiredmultiple", PackageChooserMode::RequiredMultiple },
// and a bunch of aliases
{ "zero-or-one", PackageChooserMode::Optional },
{ "radio", PackageChooserMode::Required },
{ "one", PackageChooserMode::Required },
{ "set", PackageChooserMode::OptionalMultiple },
{ "zero-or-more", PackageChooserMode::OptionalMultiple },
{ "multiple", PackageChooserMode::RequiredMultiple },
{ "one-or-more", PackageChooserMode::RequiredMultiple }
};
return names;
}
const NamedEnumTable< PackageChooserMethod >&
PackageChooserMethodNames()
{
static const NamedEnumTable< PackageChooserMethod > names {
{ "legacy", PackageChooserMethod::Legacy },
{ "custom", PackageChooserMethod::Legacy },
{ "contextualprocess", PackageChooserMethod::Legacy },
{ "packages", PackageChooserMethod::Packages },
};
return names;
}
Config::Config( QObject* parent )
: Calamares::ModuleSystem::Config( parent )
, m_model( new PackageListModel( this ) )
, m_mode( PackageChooserMode::Required )
{
}
Config::~Config() {}
const PackageItem&
Config::introductionPackage() const
{
for ( int i = 0; i < m_model->packageCount(); ++i )
{
const auto& package = m_model->packageData( i );
if ( package.isNonePackage() )
{
return package;
}
}
static PackageItem* defaultIntroduction = nullptr;
if ( !defaultIntroduction )
{
const auto name = QT_TR_NOOP( "Package Selection" );
const auto description
= QT_TR_NOOP( "Please pick a product from the list. The selected product will be installed." );
defaultIntroduction = new PackageItem( QString(), name, description );
defaultIntroduction->screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) );
defaultIntroduction->name = CalamaresUtils::Locale::TranslatedString( name, metaObject()->className() );
defaultIntroduction->description
= CalamaresUtils::Locale::TranslatedString( description, metaObject()->className() );
}
return *defaultIntroduction;
}
void
Config::updateGlobalStorage( const QStringList& selected ) const
{
if ( m_method == PackageChooserMethod::Legacy )
{
QString value = selected.join( ',' );
Calamares::JobQueue::instance()->globalStorage()->insert( m_id, value );
cDebug() << m_id<< "selected" << value;
}
else if ( m_method == PackageChooserMethod::Packages )
{
QStringList packageNames = m_model->getInstallPackagesForNames( selected );
cDebug() << m_defaultId << "packages to install" << packageNames;
CalamaresUtils::Packages::setGSPackageAdditions(
Calamares::JobQueue::instance()->globalStorage(), m_defaultId, packageNames );
}
else
{
cWarning() << "Unknown packagechooser method" << smash( m_method );
}
}
static void
fillModel( PackageListModel* model, const QVariantList& items )
{
if ( items.isEmpty() )
{
cWarning() << "No *items* for PackageChooser module.";
return;
}
#ifdef HAVE_APPSTREAM
std::unique_ptr< AppStream::Pool > pool;
bool poolOk = false;
#endif
cDebug() << "Loading PackageChooser model items from config";
int item_index = 0;
for ( const auto& item_it : items )
{
++item_index;
QVariantMap item_map = item_it.toMap();
if ( item_map.isEmpty() )
{
cWarning() << "PackageChooser entry" << item_index << "is not valid.";
continue;
}
if ( item_map.contains( "appdata" ) )
{
#ifdef HAVE_XML
model->addPackage( fromAppData( item_map ) );
#else
cWarning() << "Loading AppData XML is not supported.";
#endif
}
else if ( item_map.contains( "appstream" ) )
{
#ifdef HAVE_APPSTREAM
if ( !pool )
{
pool = std::make_unique< AppStream::Pool >();
pool->setLocale( QStringLiteral( "ALL" ) );
poolOk = pool->load();
}
if ( pool && poolOk )
{
model->addPackage( fromAppStream( *pool, item_map ) );
}
#else
cWarning() << "Loading AppStream data is not supported.";
#endif
}
else
{
model->addPackage( PackageItem( item_map ) );
}
}
cDebug() << Logger::SubEntry << "Loaded PackageChooser with" << model->packageCount() << "entries.";
}
void
Config::setConfigurationMap( const QVariantMap& configurationMap )
{
m_mode = packageChooserModeNames().find( CalamaresUtils::getString( configurationMap, "mode" ),
PackageChooserMode::Required );
m_method = PackageChooserMethodNames().find( CalamaresUtils::getString( configurationMap, "method" ),
PackageChooserMethod::Legacy );
if ( m_method == PackageChooserMethod::Legacy )
{
const QString configId = CalamaresUtils::getString( configurationMap, "id" );
const QString base = QStringLiteral( "packagechooser_" );
if ( configId.isEmpty() )
{
if ( m_defaultId.id().isEmpty() )
{
// We got nothing to work with
m_id = base;
}
else
{
m_id = base + m_defaultId.id();
}
cDebug() << "Using default ID" << m_id << "from" << m_defaultId.toString();
}
else
{
m_id = base + configId;
cDebug() << "Using configured ID" << m_id;
}
}
if ( configurationMap.contains( "items" ) )
{
fillModel( m_model, configurationMap.value( "items" ).toList() );
}
QString default_item_id = CalamaresUtils::getString( configurationMap, "default" );
if ( !default_item_id.isEmpty() )
{
for ( int item_n = 0; item_n < m_model->packageCount(); ++item_n )
{
QModelIndex item_idx = m_model->index( item_n, 0 );
QVariant item_id = m_model->data( item_idx, PackageListModel::IdRole );
if ( item_id.toString() == default_item_id )
{
m_defaultModelIndex = item_idx;
break;
}
}
}
}

View File

@ -0,0 +1,92 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef PACKAGECHOOSER_CONFIG_H
#define PACKAGECHOOSER_CONFIG_H
#include "PackageModel.h"
#include "modulesystem/Config.h"
#include "modulesystem/InstanceKey.h"
#include <memory>
enum class PackageChooserMode
{
Optional, // zero or one
Required, // exactly one
OptionalMultiple, // zero or more
RequiredMultiple // one or more
};
const NamedEnumTable< PackageChooserMode >& packageChooserModeNames();
enum class PackageChooserMethod
{
Legacy, // use contextualprocess or other custom
Packages, // use the packages module
};
const NamedEnumTable< PackageChooserMethod >& PackageChooserMethodNames();
class Config : public Calamares::ModuleSystem::Config
{
Q_OBJECT
public:
Config( QObject* parent = nullptr );
~Config() override;
/** @brief Sets the default Id for this Config
*
* The default Id is the (owning) module identifier for the config,
* and it is used when the Id read from the config file is empty.
* The **usual** configuration when using method *packages* is
* to rely on the default Id.
*/
void setDefaultId( const Calamares::ModuleSystem::InstanceKey& defaultId ) { m_defaultId = defaultId; }
void setConfigurationMap( const QVariantMap& ) override;
PackageChooserMode mode() const { return m_mode; }
PackageListModel* model() const { return m_model; }
QModelIndex defaultSelectionIndex() const { return m_defaultModelIndex; }
/** @brief Returns an "introductory package" which describes packagechooser
*
* If the model contains a "none" package, returns that one on
* the assumption that it is one to describe the whole; otherwise
* returns a totally generic description.
*/
const PackageItem& introductionPackage() const;
/** @brief Write selection to global storage
*
* Updates the GS keys for this packagechooser, marking all
* (and only) the packages in @p selected as selected.
*/
void updateGlobalStorage( const QStringList& selected ) const;
/// As updateGlobalStorage() with an empty selection list
void updateGlobalStorage() const { updateGlobalStorage( QStringList() ); }
private:
PackageListModel* m_model = nullptr;
QModelIndex m_defaultModelIndex;
/// Selection mode for this module
PackageChooserMode m_mode = PackageChooserMode::Optional;
/// How this module stores to GS
PackageChooserMethod m_method = PackageChooserMethod::Legacy;
/// Id (used to identify settings from this module in GS)
QString m_id;
/// Value to use for id if none is set in the config file
Calamares::ModuleSystem::InstanceKey m_defaultId;
};
#endif

View File

@ -10,6 +10,7 @@
#ifndef PACKAGECHOOSERPAGE_H #ifndef PACKAGECHOOSERPAGE_H
#define PACKAGECHOOSERPAGE_H #define PACKAGECHOOSERPAGE_H
#include "Config.h"
#include "PackageModel.h" #include "PackageModel.h"
#include <QAbstractItemModel> #include <QAbstractItemModel>

View File

@ -9,20 +9,22 @@
#include "PackageChooserViewStep.h" #include "PackageChooserViewStep.h"
#include "Config.h"
#include "PackageChooserPage.h"
#include "PackageModel.h"
#ifdef HAVE_XML #ifdef HAVE_XML
#include "ItemAppData.h" #include "ItemAppData.h"
#endif #endif
#ifdef HAVE_APPSTREAM #ifdef HAVE_APPSTREAM
#include "ItemAppStream.h" #include "ItemAppStream.h"
#include <AppStreamQt/pool.h> #include <AppStreamQt/pool.h>
#include <memory> #include <memory>
#endif #endif
#include "PackageChooserPage.h"
#include "PackageModel.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "locale/TranslatableConfiguration.h" #include "locale/TranslatableConfiguration.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@ -35,9 +37,8 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserViewStepFactory, registerPlug
PackageChooserViewStep::PackageChooserViewStep( QObject* parent ) PackageChooserViewStep::PackageChooserViewStep( QObject* parent )
: Calamares::ViewStep( parent ) : Calamares::ViewStep( parent )
, m_config( new Config( this ) )
, m_widget( nullptr ) , m_widget( nullptr )
, m_model( nullptr )
, m_mode( PackageChooserMode::Required )
, m_stepName( nullptr ) , m_stepName( nullptr )
{ {
emit nextStatusChanged( false ); emit nextStatusChanged( false );
@ -50,7 +51,6 @@ PackageChooserViewStep::~PackageChooserViewStep()
{ {
m_widget->deleteLater(); m_widget->deleteLater();
} }
delete m_model;
delete m_stepName; delete m_stepName;
} }
@ -67,20 +67,12 @@ PackageChooserViewStep::widget()
{ {
if ( !m_widget ) if ( !m_widget )
{ {
m_widget = new PackageChooserPage( m_mode, nullptr ); m_widget = new PackageChooserPage( m_config->mode(), nullptr );
connect( m_widget, &PackageChooserPage::selectionChanged, [=]() { connect( m_widget, &PackageChooserPage::selectionChanged, [=]() {
emit nextStatusChanged( this->isNextEnabled() ); emit nextStatusChanged( this->isNextEnabled() );
} ); } );
if ( m_model )
{
hookupModel(); hookupModel();
} }
else
{
cWarning() << "PackageChooser Widget created before model.";
}
}
return m_widget; return m_widget;
} }
@ -88,18 +80,13 @@ PackageChooserViewStep::widget()
bool bool
PackageChooserViewStep::isNextEnabled() const PackageChooserViewStep::isNextEnabled() const
{ {
if ( !m_model )
{
return false;
}
if ( !m_widget ) if ( !m_widget )
{ {
// No way to have changed anything // No way to have changed anything
return true; return true;
} }
switch ( m_mode ) switch ( m_config->mode() )
{ {
case PackageChooserMode::Optional: case PackageChooserMode::Optional:
case PackageChooserMode::OptionalMultiple: case PackageChooserMode::OptionalMultiple:
@ -139,22 +126,14 @@ PackageChooserViewStep::onActivate()
{ {
if ( !m_widget->hasSelection() ) if ( !m_widget->hasSelection() )
{ {
m_widget->setSelection( m_defaultIdx ); m_widget->setSelection( m_config->defaultSelectionIndex() );
} }
} }
void void
PackageChooserViewStep::onLeave() PackageChooserViewStep::onLeave()
{ {
QString key = QStringLiteral( "packagechooser_%1" ).arg( m_id ); m_config->updateGlobalStorage( m_widget->selectedPackageIds() );
QString value;
if ( m_widget->hasSelection() )
{
value = m_widget->selectedPackageIds().join( ',' );
}
Calamares::JobQueue::instance()->globalStorage()->insert( key, value );
cDebug() << "PackageChooser" << key << "selected" << value;
} }
Calamares::JobList Calamares::JobList
@ -167,23 +146,8 @@ PackageChooserViewStep::jobs() const
void void
PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap ) PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
QString mode = CalamaresUtils::getString( configurationMap, "mode" ); m_config->setDefaultId( moduleInstanceKey() );
bool mode_ok = false; m_config->setConfigurationMap( configurationMap );
if ( !mode.isEmpty() )
{
m_mode = roleNames().find( mode, mode_ok );
}
if ( !mode_ok )
{
m_mode = PackageChooserMode::Required;
}
m_id = CalamaresUtils::getString( configurationMap, "id" );
if ( m_id.isEmpty() )
{
// Not set, so use the instance id
m_id = moduleInstanceKey().id();
}
bool labels_ok = false; bool labels_ok = false;
auto labels = CalamaresUtils::getSubMap( configurationMap, "labels", labels_ok ); auto labels = CalamaresUtils::getSubMap( configurationMap, "labels", labels_ok );
@ -195,117 +159,22 @@ PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap
} }
} }
QString default_item_id = CalamaresUtils::getString( configurationMap, "default" ); if ( m_widget )
m_defaultIdx = QModelIndex();
bool first_time = !m_model;
if ( configurationMap.contains( "items" ) )
{
fillModel( configurationMap.value( "items" ).toList() );
}
if ( first_time && m_widget && m_model )
{ {
hookupModel(); hookupModel();
} }
// find default item
if ( first_time && m_model && !default_item_id.isEmpty() )
{
for ( int item_n = 0; item_n < m_model->packageCount(); ++item_n )
{
QModelIndex item_idx = m_model->index( item_n, 0 );
QVariant item_id = m_model->data( item_idx, PackageListModel::IdRole );
if ( item_id.toString() == default_item_id )
{
m_defaultIdx = item_idx;
break;
}
}
}
} }
void
PackageChooserViewStep::fillModel( const QVariantList& items )
{
if ( !m_model )
{
m_model = new PackageListModel( nullptr );
}
if ( items.isEmpty() )
{
cWarning() << "No *items* for PackageChooser module.";
return;
}
#ifdef HAVE_APPSTREAM
std::unique_ptr< AppStream::Pool > pool;
bool poolOk = false;
#endif
cDebug() << "Loading PackageChooser model items from config";
int item_index = 0;
for ( const auto& item_it : items )
{
++item_index;
QVariantMap item_map = item_it.toMap();
if ( item_map.isEmpty() )
{
cWarning() << "PackageChooser entry" << item_index << "is not valid.";
continue;
}
if ( item_map.contains( "appdata" ) )
{
#ifdef HAVE_XML
m_model->addPackage( fromAppData( item_map ) );
#else
cWarning() << "Loading AppData XML is not supported.";
#endif
}
else if ( item_map.contains( "appstream" ) )
{
#ifdef HAVE_APPSTREAM
if ( !pool )
{
pool = std::make_unique< AppStream::Pool >();
pool->setLocale( QStringLiteral( "ALL" ) );
poolOk = pool->load();
}
if ( pool && poolOk )
{
m_model->addPackage( fromAppStream( *pool, item_map ) );
}
#else
cWarning() << "Loading AppStream data is not supported.";
#endif
}
else
{
m_model->addPackage( PackageItem( item_map ) );
}
}
}
void void
PackageChooserViewStep::hookupModel() PackageChooserViewStep::hookupModel()
{ {
if ( !m_model || !m_widget ) if ( !m_config->model() || !m_widget )
{ {
cError() << "Can't hook up model until widget and model both exist."; cError() << "Can't hook up model until widget and model both exist.";
return; return;
} }
m_widget->setModel( m_model ); m_widget->setModel( m_config->model() );
for ( int i = 0; i < m_model->packageCount(); ++i ) m_widget->setIntroduction( m_config->introductionPackage() );
{
const auto& package = m_model->packageData( i );
if ( package.id.isEmpty() )
{
m_widget->setIntroduction( package );
break;
}
}
} }

View File

@ -15,12 +15,9 @@
#include "utils/PluginFactory.h" #include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h" #include "viewpages/ViewStep.h"
#include "PackageModel.h"
#include <QObject>
#include <QUrl>
#include <QVariantMap> #include <QVariantMap>
class Config;
class PackageChooserPage; class PackageChooserPage;
class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep
@ -49,17 +46,11 @@ public:
void setConfigurationMap( const QVariantMap& configurationMap ) override; void setConfigurationMap( const QVariantMap& configurationMap ) override;
private: private:
void fillModel( const QVariantList& items );
void hookupModel(); void hookupModel();
Config* m_config;
PackageChooserPage* m_widget; PackageChooserPage* m_widget;
PackageListModel* m_model;
// Configuration
PackageChooserMode m_mode;
QString m_id;
CalamaresUtils::Locale::TranslatedString* m_stepName; // As it appears in the sidebar CalamaresUtils::Locale::TranslatedString* m_stepName; // As it appears in the sidebar
QModelIndex m_defaultIdx;
}; };
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory ) CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory )

View File

@ -12,46 +12,20 @@
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Variant.h" #include "utils/Variant.h"
const NamedEnumTable< PackageChooserMode >&
roleNames()
{
static const NamedEnumTable< PackageChooserMode > names {
{ "optional", PackageChooserMode::Optional },
{ "required", PackageChooserMode::Required },
{ "optionalmultiple", PackageChooserMode::OptionalMultiple },
{ "requiredmultiple", PackageChooserMode::RequiredMultiple },
// and a bunch of aliases
{ "zero-or-one", PackageChooserMode::Optional },
{ "radio", PackageChooserMode::Required },
{ "one", PackageChooserMode::Required },
{ "set", PackageChooserMode::OptionalMultiple },
{ "zero-or-more", PackageChooserMode::OptionalMultiple },
{ "multiple", PackageChooserMode::RequiredMultiple },
{ "one-or-more", PackageChooserMode::RequiredMultiple }
};
return names;
}
PackageItem::PackageItem() {} PackageItem::PackageItem() {}
PackageItem::PackageItem( const QString& a_id, PackageItem::PackageItem( const QString& a_id, const QString& a_name, const QString& a_description )
const QString& a_package,
const QString& a_name,
const QString& a_description )
: id( a_id ) : id( a_id )
, package( a_package )
, name( a_name ) , name( a_name )
, description( a_description ) , description( a_description )
{ {
} }
PackageItem::PackageItem( const QString& a_id, PackageItem::PackageItem( const QString& a_id,
const QString& a_package,
const QString& a_name, const QString& a_name,
const QString& a_description, const QString& a_description,
const QString& screenshotPath ) const QString& screenshotPath )
: id( a_id ) : id( a_id )
, package( a_package )
, name( a_name ) , name( a_name )
, description( a_description ) , description( a_description )
, screenshot( screenshotPath ) , screenshot( screenshotPath )
@ -60,10 +34,10 @@ PackageItem::PackageItem( const QString& a_id,
PackageItem::PackageItem::PackageItem( const QVariantMap& item_map ) PackageItem::PackageItem::PackageItem( const QVariantMap& item_map )
: id( CalamaresUtils::getString( item_map, "id" ) ) : id( CalamaresUtils::getString( item_map, "id" ) )
, package( CalamaresUtils::getString( item_map, "package" ) )
, name( CalamaresUtils::Locale::TranslatedString( item_map, "name" ) ) , name( CalamaresUtils::Locale::TranslatedString( item_map, "name" ) )
, description( CalamaresUtils::Locale::TranslatedString( item_map, "description" ) ) , description( CalamaresUtils::Locale::TranslatedString( item_map, "description" ) )
, screenshot( CalamaresUtils::getString( item_map, "screenshot" ) ) , screenshot( CalamaresUtils::getString( item_map, "screenshot" ) )
, packageNames( CalamaresUtils::getStringList( item_map, "packages" ) )
{ {
if ( name.isEmpty() && id.isEmpty() ) if ( name.isEmpty() && id.isEmpty() )
{ {
@ -105,6 +79,33 @@ PackageListModel::addPackage( PackageItem&& p )
} }
} }
QStringList
PackageListModel::getInstallPackagesForName( const QString& id ) const
{
for ( const auto& p : qAsConst( m_packages ) )
{
if ( p.id == id )
{
return p.packageNames;
}
}
return QStringList();
}
QStringList
PackageListModel::getInstallPackagesForNames( const QStringList& ids ) const
{
QStringList l;
for ( const auto& p : qAsConst( m_packages ) )
{
if ( ids.contains( p.id ) )
{
l.append( p.packageNames );
}
}
return l;
}
int int
PackageListModel::rowCount( const QModelIndex& index ) const PackageListModel::rowCount( const QModelIndex& index ) const
{ {

View File

@ -18,24 +18,14 @@
#include <QPixmap> #include <QPixmap>
#include <QVector> #include <QVector>
enum class PackageChooserMode
{
Optional, // zero or one
Required, // exactly one
OptionalMultiple, // zero or more
RequiredMultiple // one or more
};
const NamedEnumTable< PackageChooserMode >& roleNames();
struct PackageItem struct PackageItem
{ {
QString id; QString id;
// FIXME: unused
QString package;
CalamaresUtils::Locale::TranslatedString name; CalamaresUtils::Locale::TranslatedString name;
CalamaresUtils::Locale::TranslatedString description; CalamaresUtils::Locale::TranslatedString description;
QPixmap screenshot; QPixmap screenshot;
QStringList packageNames;
/// @brief Create blank PackageItem /// @brief Create blank PackageItem
PackageItem(); PackageItem();
@ -44,7 +34,7 @@ struct PackageItem
* This constructor sets all the text members, * This constructor sets all the text members,
* but leaves the screenshot blank. Set that separately. * but leaves the screenshot blank. Set that separately.
*/ */
PackageItem( const QString& id, const QString& package, const QString& name, const QString& description ); PackageItem( const QString& id, const QString& name, const QString& description );
/** @brief Creates a PackageItem from given strings. /** @brief Creates a PackageItem from given strings.
* *
@ -52,17 +42,21 @@ struct PackageItem
* @p screenshotPath, which may be a QRC path (:/path/in/qrc) or * @p screenshotPath, which may be a QRC path (:/path/in/qrc) or
* a filesystem path, whatever QPixmap understands. * a filesystem path, whatever QPixmap understands.
*/ */
PackageItem( const QString& id, PackageItem( const QString& id, const QString& name, const QString& description, const QString& screenshotPath );
const QString& package,
const QString& name,
const QString& description,
const QString& screenshotPath );
/** @brief Creates a PackageItem from a QVariantMap /** @brief Creates a PackageItem from a QVariantMap
* *
* This is intended for use when loading PackageItems from a * This is intended for use when loading PackageItems from a
* configuration map. It will look up the various keys in the map * configuration map. It will look up the various keys in the map
* and handle translation strings as well. * and handle translation strings as well.
*
* The following keys are used:
* - *id*: the identifier for this item; if it is the empty string
* then this is the special "no-package".
* - *name* (and *name[lang]*): for the name and its translations
* - *description* (and *description[lang]*)
* - *screenshot*: a path to a screenshot for this package
* - *packages*: a list of package names
*/ */
PackageItem( const QVariantMap& map ); PackageItem( const QVariantMap& map );
@ -104,6 +98,19 @@ public:
/// @brief Direct (non-abstract) count of package data /// @brief Direct (non-abstract) count of package data
int packageCount() const { return m_packages.count(); } int packageCount() const { return m_packages.count(); }
/** @brief Does a name lookup (based on id) and returns the packages member
*
* If there is a package with the given @p id, returns its packages
* (e.g. the names of underlying packages to install for it); returns
* an empty list if the id is not found.
*/
QStringList getInstallPackagesForName( const QString& id ) const;
/** @brief Name-lookup all the @p ids and returns the packages members
*
* Concatenates installPackagesForName() for each id in @p ids.
*/
QStringList getInstallPackagesForNames( const QStringList& ids ) const;
enum Roles : int enum Roles : int
{ {
NameRole = Qt::DisplayRole, NameRole = Qt::DisplayRole,

View File

@ -3,25 +3,46 @@
# #
# Configuration for the low-density software chooser # Configuration for the low-density software chooser
--- ---
# The packagechooser writes a GlobalStorage value for the choice that # Software selection mode, to set whether the software packages
# has been made. The key is *packagechooser_<id>*. If *id* is set here, # can be chosen singly, or multiply.
# it is substituted into the key name. If it is not set, the module's #
# Possible modes are "optional", "required" (for zero-or-one or exactly-one)
# or "optionalmultiple", "requiredmultiple" (for zero-or-more
# or one-or-more).
mode: required
# Software installation method:
#
# - "legacy" or "custom" or "contextualprocess"
# When set to "legacy", writes a GlobalStorage value for the choice that
# has been made. The key is *packagechooser_<id>*. Normally, the module's
# instance name is used; see the *instances* section of `settings.conf`. # instance name is used; see the *instances* section of `settings.conf`.
# If there is just one packagechooser module, and no *id* is set, # If there is just one packagechooser module, and no special instance is set,
# resulting GS key is probably *packagechooser_packagechooser*. # resulting GS key is probably *packagechooser@packagechooser*.
# You can set *id* to change that, but it is not recommended.
# #
# The GS value is a comma-separated list of the IDs of the selected # The GS value is a comma-separated list of the IDs of the selected
# packages, or an empty string if none is selected. # packages, or an empty string if none is selected.
# #
# With "legacy" installation, you should have a contextualprocess or similar
# module somewhere in the `exec` phase to process the GlobalStorage key
# and actually **do** something for the packages.
#
# - "packages"
# When set to "packages", writes GlobalStorage values suitable for
# consumption by the *packages* module (which should appear later
# in the `exec` section. These package settings will then be handed
# off to whatever package manager is configured there.
# The *id* key is not used.
#
# There is no need to put this module in the `exec` section. There
# are no jobs that this module provides. You should put **other**
# modules, either *contextualprocess* or *packages* or some custom
# module, in the `exec` section to do the actual work.
method: legacy
# The *id* key is used only in "legacy" mode
# id: "" # id: ""
# Software selection mode, to set whether the software packages
# can be chosen singly, or multiply.
#
# Possible modes are "optional", "required" (for zero or one)
# or "optionalmultiple", "requiredmultiple" (for zero-or-more
# or one-or-more).
mode: required
# Human-visible strings in this module. These are all optional. # Human-visible strings in this module. These are all optional.
# The following translated keys are used: # The following translated keys are used:
@ -49,27 +70,45 @@ labels:
# as a source for the data. # as a source for the data.
# #
# For data provided by the list: the item has an id, which is used in # 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 # setting the value of *packagechooser_<module-id>*. The following field
# are mandatory: # is 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
# "no package selected". Only include this if the mode allows # "no package selected". Only include this if the mode allows
# selecting none. # selecting none. The name and description given for the "no package
# - *package* : Package name for the product. While mandatory, this is # selected" item are displayed when the module starts.
# not actually used anywhere.
# - *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 # Each item must adhere to one of three "styles" of item. Which styles
# you want to customize the display of that item as well. # are supported depends on compile-time dependencies of Calamares.
# Both AppData and AppStream may **optionally** be available.
#
# # Generic Items #
#
# These items are always supported. They require the most configuration
# **in this file** and duplicate information that may be available elsewhere
# (e.g. in AppData or AppStream), but do not require any additional
# dependencies. These items have the following **mandatory** fields:
#
# - *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".
#
# The following field is **optional** for an item:
#
# - *packages* :
# List of package names for the product. If using the *method*
# "packages", consider this item mandatory (because otherwise
# selecting the item would install no packages).
#
# # AppData Items #
# #
# For data provided by AppData XML: the item has an *appdata* # For data provided by AppData XML: the item has an *appdata*
# key which points to an AppData XML file in the local filesystem. # key which points to an AppData XML file in the local filesystem.
@ -84,6 +123,8 @@ labels:
# **may** specify an ID or screenshot path, as above. This will override # **may** specify an ID or screenshot path, as above. This will override
# the settings from AppData. # the settings from AppData.
# #
# # AppStream Items #
#
# For data provided by AppStream cache: the item has an *appstream* # For data provided by AppStream cache: the item has an *appstream*
# key which matches the AppStream identifier in the cache (e.g. # key which matches the AppStream identifier in the cache (e.g.
# *org.kde.kwrite.desktop*). Data is retrieved from the AppStream # *org.kde.kwrite.desktop*). Data is retrieved from the AppStream
@ -93,19 +134,19 @@ labels:
# key which will override the data from AppStream. # key which will override the data from AppStream.
items: items:
- id: "" - id: ""
package: "" # packages: [] # This item installs no packages
name: "No Desktop" name: "No Desktop"
name[nl]: "Geen desktop" name[nl]: "Geen desktop"
description: "Please pick a desktop environment from the list. If you don't want to install a desktop, that's fine, your system will start up in text-only mode and you can install a desktop environment later." description: "Please pick a desktop environment from the list. If you don't want to install a desktop, that's fine, your system will start up in text-only mode and you can install a desktop environment later."
description[nl]: "Kies eventueel een desktop-omgeving uit deze lijst. Als u geen desktop-omgeving wenst te gebruiken, kies er dan geen. In dat geval start het systeem straks op in tekst-modus en kunt u later alsnog een desktop-omgeving installeren." description[nl]: "Kies eventueel een desktop-omgeving uit deze lijst. Als u geen desktop-omgeving wenst te gebruiken, kies er dan geen. In dat geval start het systeem straks op in tekst-modus en kunt u later alsnog een desktop-omgeving installeren."
screenshot: ":/images/no-selection.png" screenshot: ":/images/no-selection.png"
- id: kde - id: kde
package: kde packages: [ kde-frameworks, kde-plasma, kde-gear ]
name: Plasma Desktop name: Plasma Desktop
description: "KDE Plasma Desktop, simple by default, a clean work area for real-world usage which intends to stay out of your way. Plasma is powerful when needed, enabling the user to create the workflow that makes them more effective to complete their tasks." description: "KDE Plasma Desktop, simple by default, a clean work area for real-world usage which intends to stay out of your way. Plasma is powerful when needed, enabling the user to create the workflow that makes them more effective to complete their tasks."
screenshot: ":/images/kde.png" screenshot: ":/images/kde.png"
- id: gnome - id: gnome
package: gnome packages: [ gnome-all ]
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"