diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 5671a591e..885915041 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -26,17 +26,22 @@ #include "utils/Logger.h" #include "utils/Yaml.h" +#include "Branding.h" #include "GlobalStorage.h" #include "Job.h" #include "JobQueue.h" #include "Settings.h" #include "ViewManager.h" +#include "modulesystem/ModuleManager.h" + #include #include #include #include #include +#include +#include #include @@ -51,13 +56,15 @@ struct ModuleConfig QString m_jobConfig; QString m_globalConfig; QString m_language; + QString m_branding; + bool m_ui; }; static ModuleConfig handle_args( QCoreApplication& a ) { QCommandLineOption debugLevelOption( - QStringLiteral( "D" ), "Verbose output for debugging purposes (0-8).", "level" ); + QStringLiteral( "D" ), "Verbose output for debugging purposes (0-8), ignored.", "level" ); QCommandLineOption globalOption( QStringList() << QStringLiteral( "g" ) << QStringLiteral( "global " ), QStringLiteral( "Global settings document" ), "global.yaml" ); @@ -67,6 +74,12 @@ handle_args( QCoreApplication& a ) QCommandLineOption langOption( QStringList() << QStringLiteral( "l" ) << QStringLiteral( "language" ), QStringLiteral( "Language (global)" ), "languagecode" ); + QCommandLineOption brandOption( QStringList() << QStringLiteral( "b" ) << QStringLiteral( "branding" ), + QStringLiteral( "Branding directory" ), + "path/to/branding.desc", + "src/branding/default/branding.desc" ); + QCommandLineOption uiOption( QStringList() << QStringLiteral( "U" ) << QStringLiteral( "ui" ), + QStringLiteral( "Enable UI" ) ); QCommandLineParser parser; parser.setApplicationDescription( "Calamares module tester" ); @@ -77,27 +90,13 @@ handle_args( QCoreApplication& a ) parser.addOption( globalOption ); parser.addOption( jobOption ); parser.addOption( langOption ); + parser.addOption( brandOption ); + parser.addOption( uiOption ); parser.addPositionalArgument( "module", "Path or name of module to run." ); parser.addPositionalArgument( "job.yaml", "Path of job settings document to use.", "[job.yaml]" ); parser.process( a ); - if ( parser.isSet( debugLevelOption ) ) - { - bool ok = true; - unsigned int l = parser.value( debugLevelOption ).toUInt( &ok ); - unsigned int dlevel = 0; - if ( !ok ) - { - dlevel = Logger::LOGVERBOSE; - } - else - { - dlevel = l; - } - Logger::setupLogLevel( dlevel ); - } - const QStringList args = parser.positionalArguments(); if ( args.isEmpty() ) { @@ -117,7 +116,12 @@ handle_args( QCoreApplication& a ) jobSettings = args.at( 1 ); } - return ModuleConfig { args.first(), jobSettings, parser.value( globalOption ), parser.value( langOption ) }; + return ModuleConfig { args.first(), + jobSettings, + parser.value( globalOption ), + parser.value( langOption ), + parser.value( brandOption ), + parser.isSet( uiOption ) }; } } @@ -184,13 +188,38 @@ load_module( const ModuleConfig& moduleConfig ) return module; } +/** @brief Create the right kind of QApplication + * + * Does primitive parsing of argv[] to find the --ui option and returns + * a UI-enabled application if it does. + * + * @p argc must be a reference (to main's argc) because the QCoreApplication + * constructors take a reference as well, and that would otherwise be a + * reference to a temporary. + */ +QCoreApplication* +createApplication( int& argc, char* argv[] ) +{ + for ( int i = 1; i < argc; ++i ) + { + if ( !qstrcmp( argv[ i ], "--ui" ) || !qstrcmp( argv[ i ], "-U" ) ) + { + auto* aw = new QApplication( argc, argv ); + aw->setQuitOnLastWindowClosed( true ); + return aw; + } + } + return new QCoreApplication( argc, argv ); +} + int main( int argc, char* argv[] ) { - QCoreApplication a( argc, argv ); - QApplication* aw = nullptr; + QCoreApplication* aw = createApplication( argc, argv ); - ModuleConfig module = handle_args( a ); + Logger::setupLogLevel( Logger::LOGVERBOSE ); + + ModuleConfig module = handle_args( *aw ); if ( module.moduleName().isEmpty() ) { return 1; @@ -198,6 +227,7 @@ main( int argc, char* argv[] ) std::unique_ptr< Calamares::Settings > settings_p( new Calamares::Settings( QString(), true ) ); std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) ); + QMainWindow* mw = nullptr; auto gs = jobqueue_p->globalStorage(); if ( !module.globalConfigFile().isEmpty() ) @@ -223,8 +253,11 @@ main( int argc, char* argv[] ) cDebug() << " .. got" << m->name() << m->typeString() << m->interfaceString(); if ( m->type() == Calamares::Module::Type::View ) { - aw = new QApplication( argc, argv ); - (void)Calamares::ViewManager::instance( nullptr ); + mw = module.m_ui ? new QMainWindow() : nullptr; + + (void)new Calamares::Branding( module.m_branding ); + (void)new Calamares::ModuleManager( QStringList(), nullptr ); + (void)Calamares::ViewManager::instance( mw ); } if ( !m->isLoaded() ) @@ -238,6 +271,16 @@ main( int argc, char* argv[] ) return 1; } + if ( mw ) + { + QWidget* w = Calamares::ViewManager::instance()->currentStep()->widget(); + w->setParent( mw ); + mw->setCentralWidget( w ); + w->show(); + mw->show(); + return aw->exec(); + } + using TR = Logger::DebugRow< const char*, const QString >; cDebug() << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() ) diff --git a/src/modules/license/LicensePage.cpp b/src/modules/license/LicensePage.cpp index 3c241e467..2cc025f83 100644 --- a/src/modules/license/LicensePage.cpp +++ b/src/modules/license/LicensePage.cpp @@ -21,11 +21,11 @@ #include "LicensePage.h" -#include "ui_LicensePage.h" #include "LicenseWidget.h" +#include "ui_LicensePage.h" -#include "JobQueue.h" #include "GlobalStorage.h" +#include "JobQueue.h" #include "ViewManager.h" #include "utils/CalamaresUtilsGui.h" @@ -36,10 +36,10 @@ #include #include +#include #include #include #include -#include #include #include @@ -47,11 +47,11 @@ const NamedEnumTable< LicenseEntry::Type >& LicenseEntry::typeNames() { - static const NamedEnumTable< LicenseEntry::Type > names{ - { QStringLiteral( "software" ), LicenseEntry::Type::Software}, + static const NamedEnumTable< LicenseEntry::Type > names { + { QStringLiteral( "software" ), LicenseEntry::Type::Software }, { QStringLiteral( "driver" ), LicenseEntry::Type::Driver }, { QStringLiteral( "gpudriver" ), LicenseEntry::Type::GpuDriver }, - { QStringLiteral( "browserplugin" ), LicenseEntry::Type::BrowserPlugin}, + { QStringLiteral( "browserplugin" ), LicenseEntry::Type::BrowserPlugin }, { QStringLiteral( "codec" ), LicenseEntry::Type::Codec }, { QStringLiteral( "package" ), LicenseEntry::Type::Package } }; @@ -59,10 +59,12 @@ LicenseEntry::typeNames() return names; } -LicenseEntry::LicenseEntry(const QVariantMap& conf) +LicenseEntry::LicenseEntry( const QVariantMap& conf ) { if ( !conf.contains( "id" ) || !conf.contains( "name" ) || !conf.contains( "url" ) ) + { return; + } m_id = conf[ "id" ].toString(); m_prettyName = conf[ "name" ].toString(); @@ -75,7 +77,9 @@ LicenseEntry::LicenseEntry(const QVariantMap& conf) QString typeString = conf.value( "type", "software" ).toString(); m_type = typeNames().find( typeString, ok ); if ( !ok ) + { cWarning() << "License entry" << m_id << "has unknown type" << typeString << "(using 'software')"; + } } bool @@ -85,7 +89,7 @@ LicenseEntry::isLocal() const } -LicensePage::LicensePage(QWidget *parent) +LicensePage::LicensePage( QWidget* parent ) : QWidget( parent ) , m_isNextEnabled( false ) , m_allLicensesOptional( false ) @@ -119,9 +123,7 @@ LicensePage::LicensePage(QWidget *parent) connect( ui->acceptCheckBox, &QCheckBox::toggled, this, &LicensePage::checkAcceptance ); - CALAMARES_RETRANSLATE( - ui->acceptCheckBox->setText( tr( "I accept the terms and conditions above." ) ); - ) + CALAMARES_RETRANSLATE( ui->acceptCheckBox->setText( tr( "I accept the terms and conditions above." ) ); ) } @@ -132,41 +134,40 @@ LicensePage::setEntries( const QList< LicenseEntry >& entriesList ) m_entries.clear(); m_entries.reserve( entriesList.count() ); - const bool required = std::any_of( entriesList.cbegin(), entriesList.cend(), []( const LicenseEntry& e ){ return e.m_required; }); + const bool required + = std::any_of( entriesList.cbegin(), entriesList.cend(), []( const LicenseEntry& e ) { return e.m_required; } ); if ( entriesList.isEmpty() ) + { m_allLicensesOptional = true; + } else + { m_allLicensesOptional = !required; + } checkAcceptance( false ); - CALAMARES_RETRANSLATE( - if ( required ) - { - ui->mainText->setText( tr( "

License Agreement

" - "This setup procedure will install proprietary " - "software that is subject to licensing terms." ) ); - ui->additionalText->setText( tr( "Please review the End User License " - "Agreements (EULAs) above.
" - "If you do not agree with the terms, the setup procedure cannot continue." ) ); - } - else - { - ui->mainText->setText( tr( "

License Agreement

" - "This setup procedure can install proprietary " - "software that is subject to licensing terms " - "in order to provide additional features and enhance the user " - "experience." ) ); - ui->additionalText->setText( tr( "Please review the End User License " - "Agreements (EULAs) above.
" - "If you do not agree with the terms, proprietary software will not " - "be installed, and open source alternatives will be used instead." ) ); - } - ui->retranslateUi( this ); + CALAMARES_RETRANSLATE( if ( required ) { + ui->mainText->setText( tr( "

License Agreement

" + "This setup procedure will install proprietary " + "software that is subject to licensing terms." ) ); + ui->additionalText->setText( tr( "Please review the End User License " + "Agreements (EULAs) above.
" + "If you do not agree with the terms, the setup procedure cannot continue." ) ); + } else { + ui->mainText->setText( tr( "

License Agreement

" + "This setup procedure can install proprietary " + "software that is subject to licensing terms " + "in order to provide additional features and enhance the user " + "experience." ) ); + ui->additionalText->setText( tr( "Please review the End User License " + "Agreements (EULAs) above.
" + "If you do not agree with the terms, proprietary software will not " + "be installed, and open source alternatives will be used instead." ) ); + } ui->retranslateUi( this ); - for ( const auto& w : m_entries ) - w->retranslateUi(); - ) + for ( const auto& w + : m_entries ) w->retranslateUi(); ) for ( const LicenseEntry& entry : entriesList ) { @@ -190,7 +191,8 @@ LicensePage::updateGlobalStorage( bool v ) Calamares::JobQueue::instance()->globalStorage()->insert( "licenseAgree", v ); } -void LicensePage::checkAcceptance( bool checked ) +void +LicensePage::checkAcceptance( bool checked ) { updateGlobalStorage( checked ); diff --git a/src/modules/license/LicensePage.h b/src/modules/license/LicensePage.h index e595f7ad8..bd9543937 100644 --- a/src/modules/license/LicensePage.h +++ b/src/modules/license/LicensePage.h @@ -24,8 +24,8 @@ #include "utils/NamedEnum.h" -#include #include +#include namespace Ui { @@ -101,4 +101,4 @@ private: QList< LicenseWidget* > m_entries; }; -#endif //LICENSEPAGE_H +#endif //LICENSEPAGE_H diff --git a/src/modules/license/LicenseViewStep.cpp b/src/modules/license/LicenseViewStep.cpp index f5f4b6e2b..89cddf27f 100644 --- a/src/modules/license/LicenseViewStep.cpp +++ b/src/modules/license/LicenseViewStep.cpp @@ -19,29 +19,30 @@ #include "LicenseViewStep.h" -#include "LicensePage.h" -#include "JobQueue.h" #include "GlobalStorage.h" +#include "JobQueue.h" +#include "LicensePage.h" #include "utils/Logger.h" #include -CALAMARES_PLUGIN_FACTORY_DEFINITION( LicenseViewStepFactory, registerPlugin(); ) +CALAMARES_PLUGIN_FACTORY_DEFINITION( LicenseViewStepFactory, registerPlugin< LicenseViewStep >(); ) LicenseViewStep::LicenseViewStep( QObject* parent ) : Calamares::ViewStep( parent ) , m_widget( new LicensePage ) { emit nextStatusChanged( false ); - connect( m_widget, &LicensePage::nextStatusChanged, - this, &LicenseViewStep::nextStatusChanged ); + connect( m_widget, &LicensePage::nextStatusChanged, this, &LicenseViewStep::nextStatusChanged ); } LicenseViewStep::~LicenseViewStep() { if ( m_widget && m_widget->parent() == nullptr ) + { m_widget->deleteLater(); + } } @@ -97,18 +98,21 @@ void LicenseViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { QList< LicenseEntry > entriesList; - if ( configurationMap.contains( "entries" ) && - configurationMap.value( "entries" ).type() == QVariant::List ) + if ( configurationMap.contains( "entries" ) && configurationMap.value( "entries" ).type() == QVariant::List ) { const auto entries = configurationMap.value( "entries" ).toList(); for ( const QVariant& entryV : entries ) { if ( entryV.type() != QVariant::Map ) + { continue; + } LicenseEntry entry( entryV.toMap() ); if ( entry.isValid() ) + { entriesList.append( entry ); + } } } diff --git a/src/modules/license/LicenseViewStep.h b/src/modules/license/LicenseViewStep.h index a4fabc8e1..957110f3d 100644 --- a/src/modules/license/LicenseViewStep.h +++ b/src/modules/license/LicenseViewStep.h @@ -20,9 +20,9 @@ #ifndef LICENSEPAGEPLUGIN_H #define LICENSEPAGEPLUGIN_H +#include #include #include -#include #include #include @@ -58,4 +58,4 @@ private: CALAMARES_PLUGIN_FACTORY_DECLARATION( LicenseViewStepFactory ) -#endif // LICENSEPAGEPLUGIN_H +#endif // LICENSEPAGEPLUGIN_H diff --git a/src/modules/license/LicenseWidget.cpp b/src/modules/license/LicenseWidget.cpp index 238d57b07..25509be95 100644 --- a/src/modules/license/LicenseWidget.cpp +++ b/src/modules/license/LicenseWidget.cpp @@ -34,10 +34,12 @@ static QString loadLocalFile( const QUrl& u ) { if ( !u.isLocalFile() ) + { return QString(); + } QFile file( u.path() ); - if ( !file.open(QIODevice::ReadOnly | QIODevice::Text) ) + if ( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { cWarning() << "Could not load license file" << u.path(); return QString(); @@ -108,11 +110,10 @@ LicenseWidget::LicenseWidget( LicenseEntry entry, QWidget* parent ) retranslateUi(); } -LicenseWidget::~LicenseWidget() -{ -} +LicenseWidget::~LicenseWidget() {} -void LicenseWidget::retranslateUi() +void +LicenseWidget::retranslateUi() { QString productDescription; switch ( m_entry.m_type ) @@ -120,40 +121,40 @@ void LicenseWidget::retranslateUi() case LicenseEntry::Type::Driver: //: %1 is an untranslatable product name, example: Creative Audigy driver productDescription = tr( "%1 driver
" - "by %2" ) - .arg( m_entry.m_prettyName ) - .arg( m_entry.m_prettyVendor ); + "by %2" ) + .arg( m_entry.m_prettyName ) + .arg( m_entry.m_prettyVendor ); break; case LicenseEntry::Type::GpuDriver: //: %1 is usually a vendor name, example: Nvidia graphics driver productDescription = tr( "%1 graphics driver
" - "by %2" ) - .arg( m_entry.m_prettyName ) - .arg( m_entry.m_prettyVendor ); + "by %2" ) + .arg( m_entry.m_prettyName ) + .arg( m_entry.m_prettyVendor ); break; case LicenseEntry::Type::BrowserPlugin: productDescription = tr( "%1 browser plugin
" - "by %2" ) - .arg( m_entry.m_prettyName ) - .arg( m_entry.m_prettyVendor ); + "by %2" ) + .arg( m_entry.m_prettyName ) + .arg( m_entry.m_prettyVendor ); break; case LicenseEntry::Type::Codec: productDescription = tr( "%1 codec
" - "by %2" ) - .arg( m_entry.m_prettyName ) - .arg( m_entry.m_prettyVendor ); + "by %2" ) + .arg( m_entry.m_prettyName ) + .arg( m_entry.m_prettyVendor ); break; case LicenseEntry::Type::Package: productDescription = tr( "%1 package
" - "by %2" ) - .arg( m_entry.m_prettyName ) - .arg( m_entry.m_prettyVendor ); + "by %2" ) + .arg( m_entry.m_prettyName ) + .arg( m_entry.m_prettyVendor ); break; case LicenseEntry::Type::Software: productDescription = tr( "%1
" - "by %2" ) - .arg( m_entry.m_prettyName ) - .arg( m_entry.m_prettyVendor ); + "by %2" ) + .arg( m_entry.m_prettyName ) + .arg( m_entry.m_prettyVendor ); } m_label->setText( productDescription ); updateExpandToolTip(); @@ -173,7 +174,9 @@ LicenseWidget::expandClicked() // Show/hide based on the new arrow direction. if ( m_fullText ) + { m_fullText->setHidden( m_expandLicenseButton->arrowType() == Qt::UpArrow ); + } updateExpandToolTip(); } @@ -186,21 +189,15 @@ LicenseWidget::updateExpandToolTip() { const bool isNowCollapsed = m_expandLicenseButton->arrowType() == Qt::UpArrow; - m_expandLicenseButton->setToolTip( - isNowCollapsed - ? tr( "Shows the complete license text" ) - : tr( "Hide license text" ) - ) ; - m_viewLicenseLabel->setText( - isNowCollapsed - ? tr( "Show license agreement" ) - : tr( "Hide license agreement" ) ); + m_expandLicenseButton->setToolTip( isNowCollapsed ? tr( "Shows the complete license text" ) + : tr( "Hide license text" ) ); + m_viewLicenseLabel->setText( isNowCollapsed ? tr( "Show license agreement" ) : tr( "Hide license agreement" ) ); } else { m_expandLicenseButton->setToolTip( tr( "Opens the license agreement in a browser window." ) ); - m_viewLicenseLabel->setText( tr( "View license agreement" ) - .arg( m_entry.m_url.toString() ) ); + m_viewLicenseLabel->setText( + tr( "View license agreement" ).arg( m_entry.m_url.toString() ) ); } } diff --git a/src/modules/license/LicenseWidget.h b/src/modules/license/LicenseWidget.h index c43233da4..e11d7a746 100644 --- a/src/modules/license/LicenseWidget.h +++ b/src/modules/license/LicenseWidget.h @@ -39,7 +39,7 @@ public: private: void expandClicked(); // "slot" to toggle show/hide of local license text - void viewClicked(); // "slot" to open link + void viewClicked(); // "slot" to open link void updateExpandToolTip(); LicenseEntry m_entry; @@ -47,5 +47,5 @@ private: QLabel* m_viewLicenseLabel; QToolButton* m_expandLicenseButton; QLabel* m_fullText; -} ; +}; #endif diff --git a/src/modules/packagechooser/PackageChooserPage.cpp b/src/modules/packagechooser/PackageChooserPage.cpp index f0f7cb04c..60becf64f 100644 --- a/src/modules/packagechooser/PackageChooserPage.cpp +++ b/src/modules/packagechooser/PackageChooserPage.cpp @@ -34,7 +34,6 @@ PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent tr( "Please pick a product from the list. The selected product will be installed." ) ) { m_introduction.screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); - cDebug() << m_introduction.screenshot; ui->setupUi( this ); CALAMARES_RETRANSLATE( updateLabels(); ) @@ -42,9 +41,9 @@ PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent switch ( mode ) { case PackageChooserMode::Optional: - case PackageChooserMode::Exclusive: + case PackageChooserMode::Required: ui->products->setSelectionMode( QAbstractItemView::SingleSelection ); - case PackageChooserMode::Multiple: + case PackageChooserMode::OptionalMultiple: case PackageChooserMode::RequiredMultiple: ui->products->setSelectionMode( QAbstractItemView::ExtendedSelection ); } @@ -87,6 +86,26 @@ void PackageChooserPage::setModel( QAbstractItemModel* model ) { ui->products->setModel( model ); + + // Check if any of the items in the model is the "none" option. + // If so, copy its values into the introduction / none item. + for ( int r = 0; r < model->rowCount(); ++r ) + { + auto index = model->index( r, 0 ); + if ( index.isValid() ) + { + QVariant v = model->data( index, PackageListModel::IdRole ); + if ( v.isValid() && v.toString().isEmpty() ) + { + m_introduction.name = model->data( index, PackageListModel::NameRole ).toString(); + m_introduction.description = model->data( index, PackageListModel::DescriptionRole ).toString(); + m_introduction.screenshot = model->data( index, PackageListModel::ScreenshotRole ).value< QPixmap >(); + currentChanged( QModelIndex() ); + break; + } + } + } + connect( ui->products->selectionModel(), &QItemSelectionModel::selectionChanged, this, @@ -98,3 +117,24 @@ PackageChooserPage::hasSelection() const { return ui && ui->products && ui->products->selectionModel() && ui->products->selectionModel()->hasSelection(); } + +QStringList +PackageChooserPage::selectedPackageIds() const +{ + if ( !( ui && ui->products && ui->products->selectionModel() ) ) + { + return QStringList(); + } + + const auto* model = ui->products->model(); + QStringList ids; + for ( const auto& index : ui->products->selectionModel()->selectedIndexes() ) + { + QString pid = model->data( index, PackageListModel::IdRole ).toString(); + if ( !pid.isEmpty() ) + { + ids.append( pid ); + } + } + return ids; +} diff --git a/src/modules/packagechooser/PackageChooserPage.h b/src/modules/packagechooser/PackageChooserPage.h index 8f9fde11f..401de903b 100644 --- a/src/modules/packagechooser/PackageChooserPage.h +++ b/src/modules/packagechooser/PackageChooserPage.h @@ -37,7 +37,13 @@ public: void setModel( QAbstractItemModel* model ); + /// @brief Is anything selected? bool hasSelection() const; + /** @brief Get the list of selected ids + * + * This list may be empty (if none is selected). + */ + QStringList selectedPackageIds() const; public slots: void currentChanged( const QModelIndex& index ); diff --git a/src/modules/packagechooser/PackageChooserViewStep.cpp b/src/modules/packagechooser/PackageChooserViewStep.cpp index 9b3c174da..8515d03a9 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.cpp +++ b/src/modules/packagechooser/PackageChooserViewStep.cpp @@ -37,7 +37,7 @@ PackageChooserViewStep::PackageChooserViewStep( QObject* parent ) : Calamares::ViewStep( parent ) , m_widget( nullptr ) , m_model( nullptr ) - , m_mode( PackageChooserMode::Exclusive ) + , m_mode( PackageChooserMode::Required ) { emit nextStatusChanged( false ); } @@ -100,10 +100,10 @@ PackageChooserViewStep::isNextEnabled() const switch ( m_mode ) { case PackageChooserMode::Optional: - case PackageChooserMode::Multiple: + case PackageChooserMode::OptionalMultiple: // zero or one OR zero or more return true; - case PackageChooserMode::Exclusive: + case PackageChooserMode::Required: case PackageChooserMode::RequiredMultiple: // exactly one OR one or more return m_widget->hasSelection(); @@ -137,6 +137,15 @@ PackageChooserViewStep::isAtEnd() const 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; } Calamares::JobList @@ -149,15 +158,40 @@ PackageChooserViewStep::jobs() const void PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { - // TODO: use the configurationMap + QString mode = CalamaresUtils::getString( configurationMap, "mode" ); + bool ok = false; + if ( !mode.isEmpty() ) + { + m_mode = roleNames().find( mode, ok ); + } + if ( !ok ) + { + m_mode = PackageChooserMode::Required; + } + m_id = CalamaresUtils::getString( configurationMap, "id" ); + if ( m_id.isEmpty() ) + { + // Not set, so use the instance id + // TODO: use a stronger type than QString for structured IDs + m_id = moduleInstanceKey().split( '@' ).last(); + } + + // TODO: replace this hard-coded model if ( !m_model ) { - m_model = new PackageListModel( nullptr ); + m_model->addPackage( PackageItem { QString(), + QString(), + "No Desktop", + "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.", + ":/images/no-selection.png" } ); m_model->addPackage( PackageItem { "kde", "kde", "Plasma", "Plasma Desktop", ":/images/kde.png" } ); - m_model->addPackage( - PackageItem { "gnome", "gnome", "GNOME", "GNU Networked Object Modeling Environment Desktop", ":/images/gnome.png" } ); + m_model->addPackage( PackageItem { + "gnome", "gnome", "GNOME", "GNU Networked Object Modeling Environment Desktop", ":/images/gnome.png" } ); if ( m_widget ) diff --git a/src/modules/packagechooser/PackageChooserViewStep.h b/src/modules/packagechooser/PackageChooserViewStep.h index ed33c3e46..55ed2d4d5 100644 --- a/src/modules/packagechooser/PackageChooserViewStep.h +++ b/src/modules/packagechooser/PackageChooserViewStep.h @@ -30,7 +30,6 @@ #include class PackageChooserPage; -class PackageListModel; class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep { @@ -61,7 +60,10 @@ private: PackageChooserPage* m_widget; PackageListModel* m_model; + + // Configuration PackageChooserMode m_mode; + QString m_id; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory ) diff --git a/src/modules/packagechooser/PackageModel.cpp b/src/modules/packagechooser/PackageModel.cpp index 7798b33bd..aa8dd39fc 100644 --- a/src/modules/packagechooser/PackageModel.cpp +++ b/src/modules/packagechooser/PackageModel.cpp @@ -20,6 +20,26 @@ #include "utils/Logger.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::fromAppStream( const QString& filename ) { @@ -108,6 +128,10 @@ PackageListModel::data( const QModelIndex& index, int role ) const { return m_packages[ row ].screenshot; } + else if ( role == IdRole ) + { + return m_packages[ row ].id; + } return QVariant(); } diff --git a/src/modules/packagechooser/PackageModel.h b/src/modules/packagechooser/PackageModel.h index 7db668df4..7f8fff8ec 100644 --- a/src/modules/packagechooser/PackageModel.h +++ b/src/modules/packagechooser/PackageModel.h @@ -19,6 +19,8 @@ #ifndef PACKAGEMODEL_H #define PACKAGEMODEL_H +#include "utils/NamedEnum.h" + #include #include #include @@ -27,11 +29,13 @@ enum class PackageChooserMode { Optional, // zero or one - Exclusive, // exactly one - Multiple, // zero or more + Required, // exactly one + OptionalMultiple, // zero or more RequiredMultiple // one or more }; +const NamedEnumTable< PackageChooserMode >& roleNames(); + struct PackageItem { QString id; @@ -69,7 +73,7 @@ class PackageListModel : public QAbstractListModel public: PackageListModel( PackageList&& items, QObject* parent ); PackageListModel( QObject* parent ); - virtual ~PackageListModel(); + virtual ~PackageListModel() override; void addPackage( PackageItem&& p ); @@ -80,7 +84,8 @@ public: { NameRole = Qt::DisplayRole, DescriptionRole = Qt::UserRole, - ScreenshotRole + ScreenshotRole, + IdRole }; private: diff --git a/src/modules/packagechooser/packagechooser.conf b/src/modules/packagechooser/packagechooser.conf new file mode 100644 index 000000000..b4e48c995 --- /dev/null +++ b/src/modules/packagechooser/packagechooser.conf @@ -0,0 +1,21 @@ +# Configuration for the low-density software chooser +--- +# The packagechooser writes a GlobalStorage value for the choice that +# has been made. The key is *packagechooser_*. If *id* is set here, +# it is substituted into the key name. If it is not set, the module's +# instance name is used; see the *instances* section of `settings.conf`. +# If there is just one packagechooser module, and no *id* is set, +# resulting GS key is probably *packagechooser_packagechooser*. +# +# The GS value is a comma-separated list of the IDs of the selected +# packages, or an empty string if none is selected. +# +# 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 diff --git a/src/modules/packagechooser/page_package.ui b/src/modules/packagechooser/page_package.ui index 1ba032ed2..17a960549 100644 --- a/src/modules/packagechooser/page_package.ui +++ b/src/modules/packagechooser/page_package.ui @@ -53,6 +53,12 @@ TextLabel + + true + + + true +