Merge branch 'software-chooser' of https://github.com/calamares/calamares into development

This commit is contained in:
Philip Müller 2019-08-04 07:02:56 +02:00
commit e7e25a11e7
15 changed files with 308 additions and 124 deletions

View File

@ -26,17 +26,22 @@
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Yaml.h" #include "utils/Yaml.h"
#include "Branding.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "Job.h" #include "Job.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "modulesystem/ModuleManager.h"
#include <QApplication> #include <QApplication>
#include <QCommandLineOption> #include <QCommandLineOption>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QCoreApplication> #include <QCoreApplication>
#include <QFileInfo> #include <QFileInfo>
#include <QLabel>
#include <QMainWindow>
#include <memory> #include <memory>
@ -51,13 +56,15 @@ struct ModuleConfig
QString m_jobConfig; QString m_jobConfig;
QString m_globalConfig; QString m_globalConfig;
QString m_language; QString m_language;
QString m_branding;
bool m_ui;
}; };
static ModuleConfig static ModuleConfig
handle_args( QCoreApplication& a ) handle_args( QCoreApplication& a )
{ {
QCommandLineOption debugLevelOption( 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 " ), QCommandLineOption globalOption( QStringList() << QStringLiteral( "g" ) << QStringLiteral( "global " ),
QStringLiteral( "Global settings document" ), QStringLiteral( "Global settings document" ),
"global.yaml" ); "global.yaml" );
@ -67,6 +74,12 @@ handle_args( QCoreApplication& a )
QCommandLineOption langOption( QStringList() << QStringLiteral( "l" ) << QStringLiteral( "language" ), QCommandLineOption langOption( QStringList() << QStringLiteral( "l" ) << QStringLiteral( "language" ),
QStringLiteral( "Language (global)" ), QStringLiteral( "Language (global)" ),
"languagecode" ); "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; QCommandLineParser parser;
parser.setApplicationDescription( "Calamares module tester" ); parser.setApplicationDescription( "Calamares module tester" );
@ -77,27 +90,13 @@ handle_args( QCoreApplication& a )
parser.addOption( globalOption ); parser.addOption( globalOption );
parser.addOption( jobOption ); parser.addOption( jobOption );
parser.addOption( langOption ); parser.addOption( langOption );
parser.addOption( brandOption );
parser.addOption( uiOption );
parser.addPositionalArgument( "module", "Path or name of module to run." ); parser.addPositionalArgument( "module", "Path or name of module to run." );
parser.addPositionalArgument( "job.yaml", "Path of job settings document to use.", "[job.yaml]" ); parser.addPositionalArgument( "job.yaml", "Path of job settings document to use.", "[job.yaml]" );
parser.process( a ); 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(); const QStringList args = parser.positionalArguments();
if ( args.isEmpty() ) if ( args.isEmpty() )
{ {
@ -117,7 +116,12 @@ handle_args( QCoreApplication& a )
jobSettings = args.at( 1 ); 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; 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 int
main( int argc, char* argv[] ) main( int argc, char* argv[] )
{ {
QCoreApplication a( argc, argv ); QCoreApplication* aw = createApplication( argc, argv );
QApplication* aw = nullptr;
ModuleConfig module = handle_args( a ); Logger::setupLogLevel( Logger::LOGVERBOSE );
ModuleConfig module = handle_args( *aw );
if ( module.moduleName().isEmpty() ) if ( module.moduleName().isEmpty() )
{ {
return 1; 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::Settings > settings_p( new Calamares::Settings( QString(), true ) );
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) ); std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
QMainWindow* mw = nullptr;
auto gs = jobqueue_p->globalStorage(); auto gs = jobqueue_p->globalStorage();
if ( !module.globalConfigFile().isEmpty() ) if ( !module.globalConfigFile().isEmpty() )
@ -223,8 +253,11 @@ main( int argc, char* argv[] )
cDebug() << " .. got" << m->name() << m->typeString() << m->interfaceString(); cDebug() << " .. got" << m->name() << m->typeString() << m->interfaceString();
if ( m->type() == Calamares::Module::Type::View ) if ( m->type() == Calamares::Module::Type::View )
{ {
aw = new QApplication( argc, argv ); mw = module.m_ui ? new QMainWindow() : nullptr;
(void)Calamares::ViewManager::instance( nullptr );
(void)new Calamares::Branding( module.m_branding );
(void)new Calamares::ModuleManager( QStringList(), nullptr );
(void)Calamares::ViewManager::instance( mw );
} }
if ( !m->isLoaded() ) if ( !m->isLoaded() )
@ -238,6 +271,16 @@ main( int argc, char* argv[] )
return 1; 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 >; using TR = Logger::DebugRow< const char*, const QString >;
cDebug() << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() ) cDebug() << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() )

View File

@ -21,11 +21,11 @@
#include "LicensePage.h" #include "LicensePage.h"
#include "ui_LicensePage.h"
#include "LicenseWidget.h" #include "LicenseWidget.h"
#include "ui_LicensePage.h"
#include "JobQueue.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
@ -36,10 +36,10 @@
#include <QApplication> #include <QApplication>
#include <QBoxLayout> #include <QBoxLayout>
#include <QComboBox>
#include <QDesktopServices> #include <QDesktopServices>
#include <QFocusEvent> #include <QFocusEvent>
#include <QLabel> #include <QLabel>
#include <QComboBox>
#include <QMessageBox> #include <QMessageBox>
#include <algorithm> #include <algorithm>
@ -47,11 +47,11 @@
const NamedEnumTable< LicenseEntry::Type >& const NamedEnumTable< LicenseEntry::Type >&
LicenseEntry::typeNames() LicenseEntry::typeNames()
{ {
static const NamedEnumTable< LicenseEntry::Type > names{ static const NamedEnumTable< LicenseEntry::Type > names {
{ QStringLiteral( "software" ), LicenseEntry::Type::Software}, { QStringLiteral( "software" ), LicenseEntry::Type::Software },
{ QStringLiteral( "driver" ), LicenseEntry::Type::Driver }, { QStringLiteral( "driver" ), LicenseEntry::Type::Driver },
{ QStringLiteral( "gpudriver" ), LicenseEntry::Type::GpuDriver }, { QStringLiteral( "gpudriver" ), LicenseEntry::Type::GpuDriver },
{ QStringLiteral( "browserplugin" ), LicenseEntry::Type::BrowserPlugin}, { QStringLiteral( "browserplugin" ), LicenseEntry::Type::BrowserPlugin },
{ QStringLiteral( "codec" ), LicenseEntry::Type::Codec }, { QStringLiteral( "codec" ), LicenseEntry::Type::Codec },
{ QStringLiteral( "package" ), LicenseEntry::Type::Package } { QStringLiteral( "package" ), LicenseEntry::Type::Package }
}; };
@ -59,10 +59,12 @@ LicenseEntry::typeNames()
return names; return names;
} }
LicenseEntry::LicenseEntry(const QVariantMap& conf) LicenseEntry::LicenseEntry( const QVariantMap& conf )
{ {
if ( !conf.contains( "id" ) || !conf.contains( "name" ) || !conf.contains( "url" ) ) if ( !conf.contains( "id" ) || !conf.contains( "name" ) || !conf.contains( "url" ) )
{
return; return;
}
m_id = conf[ "id" ].toString(); m_id = conf[ "id" ].toString();
m_prettyName = conf[ "name" ].toString(); m_prettyName = conf[ "name" ].toString();
@ -75,7 +77,9 @@ LicenseEntry::LicenseEntry(const QVariantMap& conf)
QString typeString = conf.value( "type", "software" ).toString(); QString typeString = conf.value( "type", "software" ).toString();
m_type = typeNames().find( typeString, ok ); m_type = typeNames().find( typeString, ok );
if ( !ok ) if ( !ok )
{
cWarning() << "License entry" << m_id << "has unknown type" << typeString << "(using 'software')"; cWarning() << "License entry" << m_id << "has unknown type" << typeString << "(using 'software')";
}
} }
bool bool
@ -85,7 +89,7 @@ LicenseEntry::isLocal() const
} }
LicensePage::LicensePage(QWidget *parent) LicensePage::LicensePage( QWidget* parent )
: QWidget( parent ) : QWidget( parent )
, m_isNextEnabled( false ) , m_isNextEnabled( false )
, m_allLicensesOptional( false ) , m_allLicensesOptional( false )
@ -119,9 +123,7 @@ LicensePage::LicensePage(QWidget *parent)
connect( ui->acceptCheckBox, &QCheckBox::toggled, this, &LicensePage::checkAcceptance ); connect( ui->acceptCheckBox, &QCheckBox::toggled, this, &LicensePage::checkAcceptance );
CALAMARES_RETRANSLATE( CALAMARES_RETRANSLATE( ui->acceptCheckBox->setText( tr( "I accept the terms and conditions above." ) ); )
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.clear();
m_entries.reserve( entriesList.count() ); 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() ) if ( entriesList.isEmpty() )
{
m_allLicensesOptional = true; m_allLicensesOptional = true;
}
else else
{
m_allLicensesOptional = !required; m_allLicensesOptional = !required;
}
checkAcceptance( false ); checkAcceptance( false );
CALAMARES_RETRANSLATE( CALAMARES_RETRANSLATE( if ( required ) {
if ( required ) ui->mainText->setText( tr( "<h1>License Agreement</h1>"
{ "This setup procedure will install proprietary "
ui->mainText->setText( tr( "<h1>License Agreement</h1>" "software that is subject to licensing terms." ) );
"This setup procedure will install proprietary " ui->additionalText->setText( tr( "Please review the End User License "
"software that is subject to licensing terms." ) ); "Agreements (EULAs) above.<br/>"
ui->additionalText->setText( tr( "Please review the End User License " "If you do not agree with the terms, the setup procedure cannot continue." ) );
"Agreements (EULAs) above.<br/>" } else {
"If you do not agree with the terms, the setup procedure cannot continue." ) ); ui->mainText->setText( tr( "<h1>License Agreement</h1>"
} "This setup procedure can install proprietary "
else "software that is subject to licensing terms "
{ "in order to provide additional features and enhance the user "
ui->mainText->setText( tr( "<h1>License Agreement</h1>" "experience." ) );
"This setup procedure can install proprietary " ui->additionalText->setText( tr( "Please review the End User License "
"software that is subject to licensing terms " "Agreements (EULAs) above.<br/>"
"in order to provide additional features and enhance the user " "If you do not agree with the terms, proprietary software will not "
"experience." ) ); "be installed, and open source alternatives will be used instead." ) );
ui->additionalText->setText( tr( "Please review the End User License " } ui->retranslateUi( this );
"Agreements (EULAs) above.<br/>"
"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 ) for ( const auto& w
w->retranslateUi(); : m_entries ) w->retranslateUi(); )
)
for ( const LicenseEntry& entry : entriesList ) for ( const LicenseEntry& entry : entriesList )
{ {
@ -190,7 +191,8 @@ LicensePage::updateGlobalStorage( bool v )
Calamares::JobQueue::instance()->globalStorage()->insert( "licenseAgree", v ); Calamares::JobQueue::instance()->globalStorage()->insert( "licenseAgree", v );
} }
void LicensePage::checkAcceptance( bool checked ) void
LicensePage::checkAcceptance( bool checked )
{ {
updateGlobalStorage( checked ); updateGlobalStorage( checked );

View File

@ -24,8 +24,8 @@
#include "utils/NamedEnum.h" #include "utils/NamedEnum.h"
#include <QWidget>
#include <QUrl> #include <QUrl>
#include <QWidget>
namespace Ui namespace Ui
{ {
@ -101,4 +101,4 @@ private:
QList< LicenseWidget* > m_entries; QList< LicenseWidget* > m_entries;
}; };
#endif //LICENSEPAGE_H #endif //LICENSEPAGE_H

View File

@ -19,29 +19,30 @@
#include "LicenseViewStep.h" #include "LicenseViewStep.h"
#include "LicensePage.h"
#include "JobQueue.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h"
#include "LicensePage.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include <QVariantMap> #include <QVariantMap>
CALAMARES_PLUGIN_FACTORY_DEFINITION( LicenseViewStepFactory, registerPlugin<LicenseViewStep>(); ) CALAMARES_PLUGIN_FACTORY_DEFINITION( LicenseViewStepFactory, registerPlugin< LicenseViewStep >(); )
LicenseViewStep::LicenseViewStep( QObject* parent ) LicenseViewStep::LicenseViewStep( QObject* parent )
: Calamares::ViewStep( parent ) : Calamares::ViewStep( parent )
, m_widget( new LicensePage ) , m_widget( new LicensePage )
{ {
emit nextStatusChanged( false ); emit nextStatusChanged( false );
connect( m_widget, &LicensePage::nextStatusChanged, connect( m_widget, &LicensePage::nextStatusChanged, this, &LicenseViewStep::nextStatusChanged );
this, &LicenseViewStep::nextStatusChanged );
} }
LicenseViewStep::~LicenseViewStep() LicenseViewStep::~LicenseViewStep()
{ {
if ( m_widget && m_widget->parent() == nullptr ) if ( m_widget && m_widget->parent() == nullptr )
{
m_widget->deleteLater(); m_widget->deleteLater();
}
} }
@ -97,18 +98,21 @@ void
LicenseViewStep::setConfigurationMap( const QVariantMap& configurationMap ) LicenseViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
QList< LicenseEntry > entriesList; QList< LicenseEntry > entriesList;
if ( configurationMap.contains( "entries" ) && if ( configurationMap.contains( "entries" ) && configurationMap.value( "entries" ).type() == QVariant::List )
configurationMap.value( "entries" ).type() == QVariant::List )
{ {
const auto entries = configurationMap.value( "entries" ).toList(); const auto entries = configurationMap.value( "entries" ).toList();
for ( const QVariant& entryV : entries ) for ( const QVariant& entryV : entries )
{ {
if ( entryV.type() != QVariant::Map ) if ( entryV.type() != QVariant::Map )
{
continue; continue;
}
LicenseEntry entry( entryV.toMap() ); LicenseEntry entry( entryV.toMap() );
if ( entry.isValid() ) if ( entry.isValid() )
{
entriesList.append( entry ); entriesList.append( entry );
}
} }
} }

View File

@ -20,9 +20,9 @@
#ifndef LICENSEPAGEPLUGIN_H #ifndef LICENSEPAGEPLUGIN_H
#define LICENSEPAGEPLUGIN_H #define LICENSEPAGEPLUGIN_H
#include <PluginDllMacro.h>
#include <utils/PluginFactory.h> #include <utils/PluginFactory.h>
#include <viewpages/ViewStep.h> #include <viewpages/ViewStep.h>
#include <PluginDllMacro.h>
#include <QObject> #include <QObject>
#include <QUrl> #include <QUrl>
@ -58,4 +58,4 @@ private:
CALAMARES_PLUGIN_FACTORY_DECLARATION( LicenseViewStepFactory ) CALAMARES_PLUGIN_FACTORY_DECLARATION( LicenseViewStepFactory )
#endif // LICENSEPAGEPLUGIN_H #endif // LICENSEPAGEPLUGIN_H

View File

@ -34,10 +34,12 @@ static QString
loadLocalFile( const QUrl& u ) loadLocalFile( const QUrl& u )
{ {
if ( !u.isLocalFile() ) if ( !u.isLocalFile() )
{
return QString(); return QString();
}
QFile file( u.path() ); 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(); cWarning() << "Could not load license file" << u.path();
return QString(); return QString();
@ -108,11 +110,10 @@ LicenseWidget::LicenseWidget( LicenseEntry entry, QWidget* parent )
retranslateUi(); retranslateUi();
} }
LicenseWidget::~LicenseWidget() LicenseWidget::~LicenseWidget() {}
{
}
void LicenseWidget::retranslateUi() void
LicenseWidget::retranslateUi()
{ {
QString productDescription; QString productDescription;
switch ( m_entry.m_type ) switch ( m_entry.m_type )
@ -120,40 +121,40 @@ void LicenseWidget::retranslateUi()
case LicenseEntry::Type::Driver: case LicenseEntry::Type::Driver:
//: %1 is an untranslatable product name, example: Creative Audigy driver //: %1 is an untranslatable product name, example: Creative Audigy driver
productDescription = tr( "<strong>%1 driver</strong><br/>" productDescription = tr( "<strong>%1 driver</strong><br/>"
"by %2" ) "by %2" )
.arg( m_entry.m_prettyName ) .arg( m_entry.m_prettyName )
.arg( m_entry.m_prettyVendor ); .arg( m_entry.m_prettyVendor );
break; break;
case LicenseEntry::Type::GpuDriver: case LicenseEntry::Type::GpuDriver:
//: %1 is usually a vendor name, example: Nvidia graphics driver //: %1 is usually a vendor name, example: Nvidia graphics driver
productDescription = tr( "<strong>%1 graphics driver</strong><br/>" productDescription = tr( "<strong>%1 graphics driver</strong><br/>"
"<font color=\"Grey\">by %2</font>" ) "<font color=\"Grey\">by %2</font>" )
.arg( m_entry.m_prettyName ) .arg( m_entry.m_prettyName )
.arg( m_entry.m_prettyVendor ); .arg( m_entry.m_prettyVendor );
break; break;
case LicenseEntry::Type::BrowserPlugin: case LicenseEntry::Type::BrowserPlugin:
productDescription = tr( "<strong>%1 browser plugin</strong><br/>" productDescription = tr( "<strong>%1 browser plugin</strong><br/>"
"<font color=\"Grey\">by %2</font>" ) "<font color=\"Grey\">by %2</font>" )
.arg( m_entry.m_prettyName ) .arg( m_entry.m_prettyName )
.arg( m_entry.m_prettyVendor ); .arg( m_entry.m_prettyVendor );
break; break;
case LicenseEntry::Type::Codec: case LicenseEntry::Type::Codec:
productDescription = tr( "<strong>%1 codec</strong><br/>" productDescription = tr( "<strong>%1 codec</strong><br/>"
"<font color=\"Grey\">by %2</font>" ) "<font color=\"Grey\">by %2</font>" )
.arg( m_entry.m_prettyName ) .arg( m_entry.m_prettyName )
.arg( m_entry.m_prettyVendor ); .arg( m_entry.m_prettyVendor );
break; break;
case LicenseEntry::Type::Package: case LicenseEntry::Type::Package:
productDescription = tr( "<strong>%1 package</strong><br/>" productDescription = tr( "<strong>%1 package</strong><br/>"
"<font color=\"Grey\">by %2</font>" ) "<font color=\"Grey\">by %2</font>" )
.arg( m_entry.m_prettyName ) .arg( m_entry.m_prettyName )
.arg( m_entry.m_prettyVendor ); .arg( m_entry.m_prettyVendor );
break; break;
case LicenseEntry::Type::Software: case LicenseEntry::Type::Software:
productDescription = tr( "<strong>%1</strong><br/>" productDescription = tr( "<strong>%1</strong><br/>"
"<font color=\"Grey\">by %2</font>" ) "<font color=\"Grey\">by %2</font>" )
.arg( m_entry.m_prettyName ) .arg( m_entry.m_prettyName )
.arg( m_entry.m_prettyVendor ); .arg( m_entry.m_prettyVendor );
} }
m_label->setText( productDescription ); m_label->setText( productDescription );
updateExpandToolTip(); updateExpandToolTip();
@ -173,7 +174,9 @@ LicenseWidget::expandClicked()
// Show/hide based on the new arrow direction. // Show/hide based on the new arrow direction.
if ( m_fullText ) if ( m_fullText )
{
m_fullText->setHidden( m_expandLicenseButton->arrowType() == Qt::UpArrow ); m_fullText->setHidden( m_expandLicenseButton->arrowType() == Qt::UpArrow );
}
updateExpandToolTip(); updateExpandToolTip();
} }
@ -186,21 +189,15 @@ LicenseWidget::updateExpandToolTip()
{ {
const bool isNowCollapsed = m_expandLicenseButton->arrowType() == Qt::UpArrow; const bool isNowCollapsed = m_expandLicenseButton->arrowType() == Qt::UpArrow;
m_expandLicenseButton->setToolTip( m_expandLicenseButton->setToolTip( isNowCollapsed ? tr( "Shows the complete license text" )
isNowCollapsed : tr( "Hide license text" ) );
? tr( "Shows the complete license text" ) m_viewLicenseLabel->setText( isNowCollapsed ? tr( "Show license agreement" ) : tr( "Hide license agreement" ) );
: tr( "Hide license text" )
) ;
m_viewLicenseLabel->setText(
isNowCollapsed
? tr( "Show license agreement" )
: tr( "Hide license agreement" ) );
} }
else else
{ {
m_expandLicenseButton->setToolTip( tr( "Opens the license agreement in a browser window." ) ); m_expandLicenseButton->setToolTip( tr( "Opens the license agreement in a browser window." ) );
m_viewLicenseLabel->setText( tr( "<a href=\"%1\">View license agreement</a>" ) m_viewLicenseLabel->setText(
.arg( m_entry.m_url.toString() ) ); tr( "<a href=\"%1\">View license agreement</a>" ).arg( m_entry.m_url.toString() ) );
} }
} }

View File

@ -39,7 +39,7 @@ public:
private: private:
void expandClicked(); // "slot" to toggle show/hide of local license text 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(); void updateExpandToolTip();
LicenseEntry m_entry; LicenseEntry m_entry;
@ -47,5 +47,5 @@ private:
QLabel* m_viewLicenseLabel; QLabel* m_viewLicenseLabel;
QToolButton* m_expandLicenseButton; QToolButton* m_expandLicenseButton;
QLabel* m_fullText; QLabel* m_fullText;
} ; };
#endif #endif

View File

@ -34,7 +34,6 @@ PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent
tr( "Please pick a product from the list. The selected product will be installed." ) ) tr( "Please pick a product from the list. The selected product will be installed." ) )
{ {
m_introduction.screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); m_introduction.screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) );
cDebug() << m_introduction.screenshot;
ui->setupUi( this ); ui->setupUi( this );
CALAMARES_RETRANSLATE( updateLabels(); ) CALAMARES_RETRANSLATE( updateLabels(); )
@ -42,9 +41,9 @@ PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent
switch ( mode ) switch ( mode )
{ {
case PackageChooserMode::Optional: case PackageChooserMode::Optional:
case PackageChooserMode::Exclusive: case PackageChooserMode::Required:
ui->products->setSelectionMode( QAbstractItemView::SingleSelection ); ui->products->setSelectionMode( QAbstractItemView::SingleSelection );
case PackageChooserMode::Multiple: case PackageChooserMode::OptionalMultiple:
case PackageChooserMode::RequiredMultiple: case PackageChooserMode::RequiredMultiple:
ui->products->setSelectionMode( QAbstractItemView::ExtendedSelection ); ui->products->setSelectionMode( QAbstractItemView::ExtendedSelection );
} }
@ -87,6 +86,26 @@ void
PackageChooserPage::setModel( QAbstractItemModel* model ) PackageChooserPage::setModel( QAbstractItemModel* model )
{ {
ui->products->setModel( 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(), connect( ui->products->selectionModel(),
&QItemSelectionModel::selectionChanged, &QItemSelectionModel::selectionChanged,
this, this,
@ -98,3 +117,24 @@ PackageChooserPage::hasSelection() const
{ {
return ui && ui->products && ui->products->selectionModel() && ui->products->selectionModel()->hasSelection(); 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;
}

View File

@ -37,7 +37,13 @@ public:
void setModel( QAbstractItemModel* model ); void setModel( QAbstractItemModel* model );
/// @brief Is anything selected?
bool hasSelection() const; bool hasSelection() const;
/** @brief Get the list of selected ids
*
* This list may be empty (if none is selected).
*/
QStringList selectedPackageIds() const;
public slots: public slots:
void currentChanged( const QModelIndex& index ); void currentChanged( const QModelIndex& index );

View File

@ -37,7 +37,7 @@ PackageChooserViewStep::PackageChooserViewStep( QObject* parent )
: Calamares::ViewStep( parent ) : Calamares::ViewStep( parent )
, m_widget( nullptr ) , m_widget( nullptr )
, m_model( nullptr ) , m_model( nullptr )
, m_mode( PackageChooserMode::Exclusive ) , m_mode( PackageChooserMode::Required )
{ {
emit nextStatusChanged( false ); emit nextStatusChanged( false );
} }
@ -100,10 +100,10 @@ PackageChooserViewStep::isNextEnabled() const
switch ( m_mode ) switch ( m_mode )
{ {
case PackageChooserMode::Optional: case PackageChooserMode::Optional:
case PackageChooserMode::Multiple: case PackageChooserMode::OptionalMultiple:
// zero or one OR zero or more // zero or one OR zero or more
return true; return true;
case PackageChooserMode::Exclusive: case PackageChooserMode::Required:
case PackageChooserMode::RequiredMultiple: case PackageChooserMode::RequiredMultiple:
// exactly one OR one or more // exactly one OR one or more
return m_widget->hasSelection(); return m_widget->hasSelection();
@ -137,6 +137,15 @@ PackageChooserViewStep::isAtEnd() const
void void
PackageChooserViewStep::onLeave() 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 Calamares::JobList
@ -149,15 +158,40 @@ PackageChooserViewStep::jobs() const
void void
PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap ) 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 ) if ( !m_model )
{ {
m_model = new PackageListModel( nullptr ); 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 { "kde", "kde", "Plasma", "Plasma Desktop", ":/images/kde.png" } );
m_model->addPackage( m_model->addPackage( PackageItem {
PackageItem { "gnome", "gnome", "GNOME", "GNU Networked Object Modeling Environment Desktop", ":/images/gnome.png" } ); "gnome", "gnome", "GNOME", "GNU Networked Object Modeling Environment Desktop", ":/images/gnome.png" } );
if ( m_widget ) if ( m_widget )

View File

@ -30,7 +30,6 @@
#include <QVariantMap> #include <QVariantMap>
class PackageChooserPage; class PackageChooserPage;
class PackageListModel;
class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep
{ {
@ -61,7 +60,10 @@ private:
PackageChooserPage* m_widget; PackageChooserPage* m_widget;
PackageListModel* m_model; PackageListModel* m_model;
// Configuration
PackageChooserMode m_mode; PackageChooserMode m_mode;
QString m_id;
}; };
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory ) CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory )

View File

@ -20,6 +20,26 @@
#include "utils/Logger.h" #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
PackageItem::fromAppStream( const QString& filename ) PackageItem::fromAppStream( const QString& filename )
{ {
@ -108,6 +128,10 @@ PackageListModel::data( const QModelIndex& index, int role ) const
{ {
return m_packages[ row ].screenshot; return m_packages[ row ].screenshot;
} }
else if ( role == IdRole )
{
return m_packages[ row ].id;
}
return QVariant(); return QVariant();
} }

View File

@ -19,6 +19,8 @@
#ifndef PACKAGEMODEL_H #ifndef PACKAGEMODEL_H
#define PACKAGEMODEL_H #define PACKAGEMODEL_H
#include "utils/NamedEnum.h"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QObject> #include <QObject>
#include <QPixmap> #include <QPixmap>
@ -27,11 +29,13 @@
enum class PackageChooserMode enum class PackageChooserMode
{ {
Optional, // zero or one Optional, // zero or one
Exclusive, // exactly one Required, // exactly one
Multiple, // zero or more OptionalMultiple, // zero or more
RequiredMultiple // one or more RequiredMultiple // one or more
}; };
const NamedEnumTable< PackageChooserMode >& roleNames();
struct PackageItem struct PackageItem
{ {
QString id; QString id;
@ -69,7 +73,7 @@ class PackageListModel : public QAbstractListModel
public: public:
PackageListModel( PackageList&& items, QObject* parent ); PackageListModel( PackageList&& items, QObject* parent );
PackageListModel( QObject* parent ); PackageListModel( QObject* parent );
virtual ~PackageListModel(); virtual ~PackageListModel() override;
void addPackage( PackageItem&& p ); void addPackage( PackageItem&& p );
@ -80,7 +84,8 @@ public:
{ {
NameRole = Qt::DisplayRole, NameRole = Qt::DisplayRole,
DescriptionRole = Qt::UserRole, DescriptionRole = Qt::UserRole,
ScreenshotRole ScreenshotRole,
IdRole
}; };
private: private:

View File

@ -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_<id>*. 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

View File

@ -53,6 +53,12 @@
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>