diff --git a/src/modules/packagechooser/CMakeLists.txt b/src/modules/packagechooser/CMakeLists.txt index ad9cc8527..e6c2c5b1d 100644 --- a/src/modules/packagechooser/CMakeLists.txt +++ b/src/modules/packagechooser/CMakeLists.txt @@ -45,6 +45,7 @@ calamares_add_plugin( packagechooser TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES + Config.cpp PackageChooserPage.cpp PackageChooserViewStep.cpp PackageModel.cpp diff --git a/src/modules/packagechooser/Config.cpp b/src/modules/packagechooser/Config.cpp new file mode 100644 index 000000000..aa383d3c8 --- /dev/null +++ b/src/modules/packagechooser/Config.cpp @@ -0,0 +1,170 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * 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 "utils/Logger.h" +#include "utils/Variant.h" + + +Config::Config( const QString& defaultId, QObject* parent ) + : Calamares::ModuleSystem::Config( parent ) + , m_model( new PackageListModel( this ) ) + , m_mode( PackageChooserMode::Required ) + , m_defaultId( defaultId ) +{ +} + +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 ) + { + defaultIntroduction = new PackageItem( + QString(), + QT_TR_NOOP( "Package Selection" ), + QT_TR_NOOP( "Please pick a product from the list. The selected product will be installed." ) ); + defaultIntroduction->screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); + // TODO: enable better translation + // defaultIntroduction->name.setContext( metaObject()->className() ); + // defaultIntroduction->description.setContext( metaObject()->className() ); + } + return *defaultIntroduction; +} + +void +Config::updateGlobalStorage( const QStringList& selected ) const +{ + QString key = QStringLiteral( "packagechooser_%1" ).arg( m_id ); + QString value = selected.join( ',' ); + Calamares::JobQueue::instance()->globalStorage()->insert( key, value ); + + cDebug() << "PackageChooser" << key << "selected" << value; +} + + +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 ) ); + } + } +} + +void +Config::setConfigurationMap( const QVariantMap& configurationMap ) +{ + QString mode = CalamaresUtils::getString( configurationMap, "mode" ); + bool mode_ok = false; + if ( !mode.isEmpty() ) + { + m_mode = packageChooserModeNames().find( mode, mode_ok ); + } + if ( !mode_ok ) + { + m_mode = PackageChooserMode::Required; + } + + m_id = CalamaresUtils::getString( configurationMap, "id" ); + if ( m_id.isEmpty() ) + { + m_id = m_defaultId; + } + + m_defaultModelIndex = QModelIndex(); + if ( configurationMap.contains( "items" ) ) + { + fillModel( m_model, configurationMap.value( "items" ).toList() ); + } + + QString default_item_id = CalamaresUtils::getString( configurationMap, "default" ); + // find default item + 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; + } + } + } +} diff --git a/src/modules/packagechooser/Config.h b/src/modules/packagechooser/Config.h new file mode 100644 index 000000000..6a65c8788 --- /dev/null +++ b/src/modules/packagechooser/Config.h @@ -0,0 +1,61 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * 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 + +class Config : public Calamares::ModuleSystem::Config +{ + Q_OBJECT + +public: + Config( const QString& defaultId, QObject* parent = nullptr ); + ~Config() override; + + 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; + + // Configuration + PackageChooserMode m_mode = PackageChooserMode::Optional; + QString m_id; + QString m_defaultId; +}; + + +#endif diff --git a/src/modules/packagechooser/PackageChooserViewStep.cpp b/src/modules/packagechooser/PackageChooserViewStep.cpp index d576f2753..05d0d3cfd 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.cpp +++ b/src/modules/packagechooser/PackageChooserViewStep.cpp @@ -9,20 +9,22 @@ #include "PackageChooserViewStep.h" +#include "Config.h" +#include "PackageChooserPage.h" +#include "PackageModel.h" + #ifdef HAVE_XML #include "ItemAppData.h" #endif + #ifdef HAVE_APPSTREAM #include "ItemAppStream.h" #include #include #endif -#include "PackageChooserPage.h" -#include "PackageModel.h" #include "GlobalStorage.h" #include "JobQueue.h" - #include "locale/TranslatableConfiguration.h" #include "utils/CalamaresUtilsSystem.h" #include "utils/Logger.h" @@ -35,9 +37,8 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserViewStepFactory, registerPlug PackageChooserViewStep::PackageChooserViewStep( QObject* parent ) : Calamares::ViewStep( parent ) + , m_config( new Config( moduleInstanceKey().id(), this ) ) , m_widget( nullptr ) - , m_model( nullptr ) - , m_mode( PackageChooserMode::Required ) , m_stepName( nullptr ) { emit nextStatusChanged( false ); @@ -50,7 +51,6 @@ PackageChooserViewStep::~PackageChooserViewStep() { m_widget->deleteLater(); } - delete m_model; delete m_stepName; } @@ -67,19 +67,10 @@ PackageChooserViewStep::widget() { if ( !m_widget ) { - m_widget = new PackageChooserPage( m_mode, nullptr ); + m_widget = new PackageChooserPage( m_config->mode(), nullptr ); connect( m_widget, &PackageChooserPage::selectionChanged, [=]() { emit nextStatusChanged( this->isNextEnabled() ); } ); - - if ( m_model ) - { - hookupModel(); - } - else - { - cWarning() << "PackageChooser Widget created before model."; - } } return m_widget; } @@ -88,18 +79,13 @@ PackageChooserViewStep::widget() bool PackageChooserViewStep::isNextEnabled() const { - if ( !m_model ) - { - return false; - } - if ( !m_widget ) { // No way to have changed anything return true; } - switch ( m_mode ) + switch ( m_config->mode() ) { case PackageChooserMode::Optional: case PackageChooserMode::OptionalMultiple: @@ -139,22 +125,14 @@ PackageChooserViewStep::onActivate() { if ( !m_widget->hasSelection() ) { - m_widget->setSelection( m_defaultIdx ); + m_widget->setSelection( m_config->defaultSelectionIndex() ); } } void PackageChooserViewStep::onLeave() { - QString key = QStringLiteral( "packagechooser_%1" ).arg( m_id ); - QString value; - if ( m_widget->hasSelection() ) - { - value = m_widget->selectedPackageIds().join( ',' ); - } - Calamares::JobQueue::instance()->globalStorage()->insert( key, value ); - - cDebug() << "PackageChooser" << key << "selected" << value; + m_config->updateGlobalStorage( m_widget->selectedPackageIds() ); } Calamares::JobList @@ -167,23 +145,7 @@ PackageChooserViewStep::jobs() const void PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { - QString mode = CalamaresUtils::getString( configurationMap, "mode" ); - bool mode_ok = false; - 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(); - } + m_config->setConfigurationMap( configurationMap ); bool labels_ok = false; auto labels = CalamaresUtils::getSubMap( configurationMap, "labels", labels_ok ); @@ -195,117 +157,22 @@ PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap } } - QString default_item_id = CalamaresUtils::getString( configurationMap, "default" ); - m_defaultIdx = QModelIndex(); - - bool first_time = !m_model; - if ( configurationMap.contains( "items" ) ) - { - fillModel( configurationMap.value( "items" ).toList() ); - } - - if ( first_time && m_widget && m_model ) + if ( m_widget ) { 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 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."; return; } - m_widget->setModel( m_model ); - for ( int i = 0; i < m_model->packageCount(); ++i ) - { - const auto& package = m_model->packageData( i ); - if ( package.id.isEmpty() ) - { - m_widget->setIntroduction( package ); - break; - } - } + m_widget->setModel( m_config->model() ); + m_widget->setIntroduction( m_config->introductionPackage() ); } diff --git a/src/modules/packagechooser/PackageChooserViewStep.h b/src/modules/packagechooser/PackageChooserViewStep.h index 9dfd2bdee..7561f2bd7 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.h +++ b/src/modules/packagechooser/PackageChooserViewStep.h @@ -15,12 +15,9 @@ #include "utils/PluginFactory.h" #include "viewpages/ViewStep.h" -#include "PackageModel.h" - -#include -#include #include +class Config; class PackageChooserPage; class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep @@ -49,17 +46,11 @@ public: void setConfigurationMap( const QVariantMap& configurationMap ) override; private: - void fillModel( const QVariantList& items ); void hookupModel(); + Config* m_config; PackageChooserPage* m_widget; - PackageListModel* m_model; - - // Configuration - PackageChooserMode m_mode; - QString m_id; CalamaresUtils::Locale::TranslatedString* m_stepName; // As it appears in the sidebar - QModelIndex m_defaultIdx; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory ) diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index bb48ab888..f1c8b3056 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -13,7 +13,7 @@ #include "utils/Variant.h" const NamedEnumTable< PackageChooserMode >& -roleNames() +packageChooserModeNames() { static const NamedEnumTable< PackageChooserMode > names { { "optional", PackageChooserMode::Optional }, diff --git a/src/modules/packagechooser/PackageModel.h b/src/modules/packagechooser/PackageModel.h index fc1d787f1..0ced3ffb3 100644 --- a/src/modules/packagechooser/PackageModel.h +++ b/src/modules/packagechooser/PackageModel.h @@ -26,7 +26,7 @@ enum class PackageChooserMode RequiredMultiple // one or more }; -const NamedEnumTable< PackageChooserMode >& roleNames(); +const NamedEnumTable< PackageChooserMode >& packageChooserModeNames(); struct PackageItem {