Merge branch 'master' of https://github.com/calamares/calamares into development

This commit is contained in:
Philip Müller 2019-06-02 09:28:30 +02:00
commit 36ce87e1dc
20 changed files with 201 additions and 87 deletions

View File

@ -36,8 +36,6 @@ and moral support from (alphabetically by first name or nickname):
- Kyle Robertze - Kyle Robertze
- Lisa Vitolo - Lisa Vitolo
- n3rdopolis - n3rdopolis
- Phil Mueller
- Philip Mueller
- Philip Müller - Philip Müller
- Ramon Buldó - Ramon Buldó
- Raul Rodrigo Segura - Raul Rodrigo Segura

View File

@ -10,6 +10,9 @@ This release contains contributions from (alphabetically by first name):
## Core ## ## Core ##
No user- or deployer-visible changes. Bugfixing as usual, see the
milestone for details.
## Modules ## ## Modules ##
- *branding* now supports os-release variables in the *strings* section, - *branding* now supports os-release variables in the *strings* section,
@ -19,6 +22,9 @@ This release contains contributions from (alphabetically by first name):
*productLogo* and *productIcon* keys. If a file is named there, then *productLogo* and *productIcon* keys. If a file is named there, then
the file is used, and otherwise the icon is looked up in the current the file is used, and otherwise the icon is looked up in the current
theme. theme.
- *welcome* allows a custom image path or icon name to be set for the
language-selection drop-down (instead of the international standard one).
# 3.2.8 (2019-05-10) # # 3.2.8 (2019-05-10) #

View File

@ -60,6 +60,8 @@ some PEP8 guidelines.
* Function and class definitions have their braces on separate lines. * Function and class definitions have their braces on separate lines.
* A function implementation's return type is on its own line. * A function implementation's return type is on its own line.
* `CamelCase.{cpp,h}` style file names. * `CamelCase.{cpp,h}` style file names.
* Lambdas are preferrably indented to a 4-space tab, even when passed as an
argument to functions.
Example: Example:
``` ```

View File

@ -125,8 +125,8 @@ LabelModel::find( const QString& countryCode ) const
LabelModel* availableTranslations() LabelModel* availableTranslations()
{ {
static LabelModel model( QString( CALAMARES_TRANSLATION_LANGUAGES ).split( ';') ); static LabelModel* model = new LabelModel( QString( CALAMARES_TRANSLATION_LANGUAGES ).split( ';') );
return &model; return model;
} }
} }

View File

@ -129,16 +129,16 @@ Branding::Branding( const QString& brandingFilePath,
, m_welcomeExpandingLogo( true ) , m_welcomeExpandingLogo( true )
{ {
cDebug() << "Using Calamares branding file at" << brandingFilePath; cDebug() << "Using Calamares branding file at" << brandingFilePath;
QDir componentDir( componentDirectory() );
if ( !componentDir.exists() )
bail( "Bad component directory path." );
QFile file( brandingFilePath ); QFile file( brandingFilePath );
if ( file.exists() && file.open( QFile::ReadOnly | QFile::Text ) ) if ( file.exists() && file.open( QFile::ReadOnly | QFile::Text ) )
{ {
QByteArray ba = file.readAll(); QByteArray ba = file.readAll();
QFileInfo fi ( m_descriptorPath );
QDir componentDir = fi.absoluteDir();
if ( !componentDir.exists() )
bail( "Bad component directory path." );
try try
{ {
YAML::Node doc = YAML::Load( ba.constData() ); YAML::Node doc = YAML::Load( ba.constData() );
@ -146,7 +146,7 @@ Branding::Branding( const QString& brandingFilePath,
m_componentName = QString::fromStdString( doc[ "componentName" ] m_componentName = QString::fromStdString( doc[ "componentName" ]
.as< std::string >() ); .as< std::string >() );
if ( m_componentName != QFileInfo( m_descriptorPath ).absoluteDir().dirName() ) if ( m_componentName != componentDir.dirName() )
bail( "The branding component name should match the name of the " bail( "The branding component name should match the name of the "
"component directory." ); "component directory." );
@ -186,6 +186,7 @@ Branding::Branding( const QString& brandingFilePath,
loadStrings( m_images, doc, "images", loadStrings( m_images, doc, "images",
[&]( const QString& s ) -> QString [&]( const QString& s ) -> QString
{ {
// See also image()
const QString imageName( expand( s ) ); const QString imageName( expand( s ) );
QFileInfo imageFi( componentDir.absoluteFilePath( imageName ) ); QFileInfo imageFi( componentDir.absoluteFilePath( imageName ) );
if ( !imageFi.exists() ) if ( !imageFi.exists() )
@ -314,6 +315,22 @@ Branding::image( Branding::ImageEntry imageEntry, const QSize& size ) const
} }
} }
QPixmap
Branding::image(const QString& imageName, const QSize& size) const
{
QDir componentDir( componentDirectory() );
QFileInfo imageFi( componentDir.absoluteFilePath( imageName ) );
if ( !imageFi.exists() )
{
const auto icon = QIcon::fromTheme( imageName );
// Not found, bail out with the filename used
if ( icon.isNull() )
return QPixmap();
return icon.pixmap( size );
}
return ImageRegistry::instance()->pixmap( imageFi.absoluteFilePath(), size );
}
QString QString
Branding::stylesheet() const Branding::stylesheet() const
{ {

View File

@ -123,6 +123,16 @@ public:
QString styleString( Branding::StyleEntry styleEntry ) const; QString styleString( Branding::StyleEntry styleEntry ) const;
QString imagePath( Branding::ImageEntry imageEntry ) const; QString imagePath( Branding::ImageEntry imageEntry ) const;
QPixmap image( Branding::ImageEntry imageEntry, const QSize& size ) const; QPixmap image( Branding::ImageEntry imageEntry, const QSize& size ) const;
/** @brief Look up an image in the branding directory or as an icon
*
* The @p name is checked in the branding directory: if it is an image
* file, return the pixmap from that file, at the requested size.
* If it isn't a file, look it up as an icon name in the current theme.
* May return a null pixmap if nothing is found.
*/
QPixmap image( const QString& name, const QSize& size ) const;
/** @brief Stylesheet to apply for this branding. May be empty. /** @brief Stylesheet to apply for this branding. May be empty.
* *
* The file is loaded every time this function is called, so * The file is loaded every time this function is called, so

View File

@ -23,9 +23,13 @@
#include "core/PartitionInfo.h" #include "core/PartitionInfo.h"
#include "core/KPMHelpers.h" #include "core/KPMHelpers.h"
#include "utils/Logger.h"
// KPMcore // KPMcore
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <QComboBox>
static QStandardItem* static QStandardItem*
createBootLoaderItem( const QString& description, const QString& path, bool isPartition ) createBootLoaderItem( const QString& description, const QString& path, bool isPartition )
{ {
@ -47,12 +51,12 @@ BootLoaderModel::~BootLoaderModel()
void void
BootLoaderModel::init( const QList< Device* >& devices ) BootLoaderModel::init( const QList< Device* >& devices )
{ {
cDebug() << "BLM::init with" << devices.count() << "devices" << rowCount() << "rows";
beginResetModel(); beginResetModel();
blockSignals( true ); blockSignals( true );
m_devices = devices; m_devices = devices;
clear(); updateInternal();
createMbrItems();
blockSignals( false ); blockSignals( false );
endResetModel(); endResetModel();
@ -74,6 +78,7 @@ BootLoaderModel::createMbrItems()
void void
BootLoaderModel::update() BootLoaderModel::update()
{ {
cDebug() << "BLM::update holds" << m_devices.count() << "devices" << rowCount() << "rows";
beginResetModel(); beginResetModel();
blockSignals( true ); blockSignals( true );
updateInternal(); updateInternal();
@ -89,6 +94,10 @@ BootLoaderModel::updateInternal()
clear(); clear();
createMbrItems(); createMbrItems();
// An empty model is possible if you don't havee permissions: don't crash though.
if ( rowCount() < 1 )
return;
QString partitionText; QString partitionText;
Partition* partition = KPMHelpers::findPartitionByMountPoint( m_devices, "/boot" ); Partition* partition = KPMHelpers::findPartitionByMountPoint( m_devices, "/boot" );
if ( partition ) if ( partition )
@ -148,3 +157,48 @@ BootLoaderModel::data( const QModelIndex& index, int role ) const
} }
return QStandardItemModel::data( index, role ); return QStandardItemModel::data( index, role );
} }
namespace Calamares
{
int
findBootloader( const QAbstractItemModel* model, const QString& path )
{
for ( int i = 0; i < model->rowCount(); ++i)
{
const auto index = model->index( i, 0, QModelIndex() );
if ( !index.isValid() )
continue;
QVariant var = model->data( index, BootLoaderModel::BootLoaderPathRole );
if ( var.isValid() && var.toString() == path )
return i;
}
return -1;
}
void
restoreSelectedBootLoader( QComboBox& combo, const QString& path )
{
const auto* model = combo.model();
if ( model->rowCount() < 1 )
{
cDebug() << "No items in BootLoaderModel";
return;
}
int r = -1;
if ( path.isEmpty() )
{
combo.setCurrentIndex( 0 );
}
else if ( (r = findBootloader( model, path )) >= 0 )
{
combo.setCurrentIndex( r );
}
else
{
combo.setCurrentIndex( 0 );
}
}
} // namespace

View File

@ -25,6 +25,7 @@
#include <QStandardItemModel> #include <QStandardItemModel>
class Device; class Device;
class QComboBox;
/** /**
* This model contains one entry for each device MBR plus one entry for the * This model contains one entry for each device MBR plus one entry for the
@ -63,4 +64,20 @@ private:
void updateInternal(); void updateInternal();
}; };
namespace Calamares
{
/** @brief Returns the row number of boot-loader @p path (e.g. /dev/sda)
*
* Assuming the @p model is a BootLoaderModel, will return a row number
* in the model. Returns -1 otherwise.
*/
int findBootloader( const QAbstractItemModel* model, const QString& path );
/** @brief Tries to set @p path as selected item in @p combo
*
* Matches a boot-loader install path (e.g. /dev/sda) with a model
* row and sets that as the current row.
*/
void restoreSelectedBootLoader( QComboBox& combo, const QString& path );
} // namespace
#endif /* BOOTLOADERMODEL_H */ #endif /* BOOTLOADERMODEL_H */

View File

@ -22,6 +22,8 @@
#include "core/KPMHelpers.h" #include "core/KPMHelpers.h"
#include "core/PartitionIterator.h" #include "core/PartitionIterator.h"
#include "utils/Logger.h"
// KPMcore // KPMcore
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/fs/luks.h> #include <kpmcore/fs/luks.h>
@ -81,6 +83,12 @@ _findRootForPartition( PartitionNode* partition )
QColor QColor
colorForPartition( Partition* partition ) colorForPartition( Partition* partition )
{ {
if ( !partition )
{
cWarning() << "NULL partition";
return FREE_SPACE_COLOR;
}
if ( KPMHelpers::isPartitionFreeSpace( partition ) ) if ( KPMHelpers::isPartitionFreeSpace( partition ) )
return FREE_SPACE_COLOR; return FREE_SPACE_COLOR;
if ( partition->roles().has( PartitionRole::Extended ) ) if ( partition->roles().has( PartitionRole::Extended ) )

View File

@ -402,7 +402,7 @@ PartitionCoreModule::deletePartition( Device* device, Partition* partition )
deletePartition( device, childPartition ); deletePartition( device, childPartition );
} }
QList< Calamares::job_ptr >& jobs = deviceInfo->jobs; Calamares::JobList& jobs = deviceInfo->jobs;
if ( partition->state() == KPM_PARTITION_STATE(New) ) if ( partition->state() == KPM_PARTITION_STATE(New) )
{ {
// First remove matching SetPartFlagsJobs // First remove matching SetPartFlagsJobs
@ -496,10 +496,10 @@ PartitionCoreModule::setPartitionFlags( Device* device,
PartitionInfo::setFlags( partition, flags ); PartitionInfo::setFlags( partition, flags );
} }
QList< Calamares::job_ptr > Calamares::JobList
PartitionCoreModule::jobs() const PartitionCoreModule::jobs() const
{ {
QList< Calamares::job_ptr > lst; Calamares::JobList lst;
QList< Device* > devices; QList< Device* > devices;
#ifdef DEBUG_PARTITION_UNSAFE #ifdef DEBUG_PARTITION_UNSAFE
@ -947,12 +947,7 @@ PartitionCoreModule::revertDevice( Device* dev, bool individualRevert )
QList< Device* > devices; QList< Device* > devices;
for ( DeviceInfo* const info : m_deviceInfos ) for ( DeviceInfo* const info : m_deviceInfos )
{ {
// device is a QScopedPointer if ( info && !info->device.isNull() && info->device->type() == Device::Type::Disk_Device )
if ( !info || info->device.isNull() )
continue;
if ( info->device->type() != Device::Type::Disk_Device )
continue;
else
devices.append( info->device.data() ); devices.append( info->device.data() );
} }

View File

@ -247,7 +247,7 @@ private:
QScopedPointer< Device > device; QScopedPointer< Device > device;
QScopedPointer< PartitionModel > partitionModel; QScopedPointer< PartitionModel > partitionModel;
const QScopedPointer< Device > immutableDevice; const QScopedPointer< Device > immutableDevice;
QList< Calamares::job_ptr > jobs; Calamares::JobList jobs;
// To check if LVM VGs are deactivated // To check if LVM VGs are deactivated
bool isAvailable; bool isAvailable;

View File

@ -960,19 +960,18 @@ ChoicePage::updateActionChoicePreview( ChoicePage::InstallChoice choice )
QLabel* sizeLabel = new QLabel( m_previewAfterFrame ); QLabel* sizeLabel = new QLabel( m_previewAfterFrame );
layout->addWidget( sizeLabel ); layout->addWidget( sizeLabel );
sizeLabel->setWordWrap( true ); sizeLabel->setWordWrap( true );
connect( m_afterPartitionSplitterWidget, &PartitionSplitterWidget::partitionResized, connect( m_afterPartitionSplitterWidget, &PartitionSplitterWidget::partitionResized, this,
this, [ this, sizeLabel ]( const QString& path, [ this, sizeLabel ]( const QString& path, qint64 size, qint64 sizeNext )
qint64 size, {
qint64 sizeNext ) Q_UNUSED( path )
{ sizeLabel->setText( tr( "%1 will be shrunk to %2MiB and a new "
Q_UNUSED( path ) "%3MiB partition will be created for %4." )
sizeLabel->setText( tr( "%1 will be shrunk to %2MiB and a new " .arg( m_beforePartitionBarsView->selectionModel()->currentIndex().data().toString() )
"%3MiB partition will be created for %4." ) .arg( CalamaresUtils::BytesToMiB( size ) )
.arg( m_beforePartitionBarsView->selectionModel()->currentIndex().data().toString() ) .arg( CalamaresUtils::BytesToMiB( sizeNext ) )
.arg( CalamaresUtils::BytesToMiB( size ) ) .arg( *Calamares::Branding::ShortProductName ) );
.arg( CalamaresUtils::BytesToMiB( sizeNext ) ) }
.arg( *Calamares::Branding::ShortProductName ) ); );
} );
m_previewAfterFrame->show(); m_previewAfterFrame->show();
m_previewAfterLabel->show(); m_previewAfterLabel->show();
@ -1025,18 +1024,25 @@ ChoicePage::updateActionChoicePreview( ChoicePage::InstallChoice choice )
eraseBootloaderLabel->setText( tr( "Boot loader location:" ) ); eraseBootloaderLabel->setText( tr( "Boot loader location:" ) );
m_bootloaderComboBox = createBootloaderComboBox( eraseWidget ); m_bootloaderComboBox = createBootloaderComboBox( eraseWidget );
connect( m_core, &PartitionCoreModule::deviceReverted, connect( m_core->bootLoaderModel(), &QAbstractItemModel::modelReset,
this, [ this ]( Device* dev ) [ this ]()
{
Q_UNUSED( dev )
if ( !m_bootloaderComboBox.isNull() )
{ {
if ( m_bootloaderComboBox->model() != m_core->bootLoaderModel() ) if ( !m_bootloaderComboBox.isNull() )
m_bootloaderComboBox->setModel( m_core->bootLoaderModel() ); Calamares::restoreSelectedBootLoader( *m_bootloaderComboBox, m_core->bootLoaderInstallPath() );
m_bootloaderComboBox->setCurrentIndex( m_lastSelectedDeviceIndex );
} }
}, Qt::QueuedConnection ); );
connect( m_core, &PartitionCoreModule::deviceReverted, this,
[ this ]( Device* dev )
{
Q_UNUSED( dev )
if ( !m_bootloaderComboBox.isNull() )
{
if ( m_bootloaderComboBox->model() != m_core->bootLoaderModel() )
m_bootloaderComboBox->setModel( m_core->bootLoaderModel() );
m_bootloaderComboBox->setCurrentIndex( m_lastSelectedDeviceIndex );
}
}, Qt::QueuedConnection );
// ^ Must be Queued so it's sure to run when the widget is already visible. // ^ Must be Queued so it's sure to run when the widget is already visible.
eraseLayout->addWidget( m_bootloaderComboBox ); eraseLayout->addWidget( m_bootloaderComboBox );

View File

@ -219,7 +219,7 @@ PartitionPage::onNewPartitionTableClicked()
m_core->createPartitionTable( device, type ); m_core->createPartitionTable( device, type );
} }
delete dlg; delete dlg;
// PartionModelReset isn't emmited after createPartitionTable, so we have to manually update // PartionModelReset isn't emitted after createPartitionTable, so we have to manually update
// the bootLoader index after the reset. // the bootLoader index after the reset.
updateBootLoaderIndex(); updateBootLoaderIndex();
} }
@ -511,45 +511,10 @@ PartitionPage::updateSelectedBootLoaderIndex()
cDebug() << "Selected bootloader index" << m_lastSelectedBootLoaderIndex; cDebug() << "Selected bootloader index" << m_lastSelectedBootLoaderIndex;
} }
int
findBootloader( const QAbstractItemModel* model, const QString& path )
{
for ( int i = 0; i < model->rowCount(); ++i)
{
const auto index = model->index( i, 0, QModelIndex() );
if ( !index.isValid() )
continue;
QVariant var = model->data( index, BootLoaderModel::BootLoaderPathRole );
if ( var.isValid() && var.toString() == path )
return i;
}
return -1;
}
void void
PartitionPage::restoreSelectedBootLoader() PartitionPage::restoreSelectedBootLoader()
{ {
const auto* model = m_ui->bootLoaderComboBox->model(); Calamares::restoreSelectedBootLoader( *(m_ui->bootLoaderComboBox), m_core->bootLoaderInstallPath() );
if ( model->rowCount() < 1 )
{
cDebug() << "No items in BootLoaderModel";
return;
}
int r = -1;
if ( m_core->bootLoaderInstallPath().isEmpty() )
{
m_ui->bootLoaderComboBox->setCurrentIndex( 0 );
}
else if ( (r = findBootloader( model, m_core->bootLoaderInstallPath() )) >= 0 )
{
m_ui->bootLoaderComboBox->setCurrentIndex( r );
}
else
{
m_ui->bootLoaderComboBox->setCurrentIndex( 0 );
}
} }

View File

@ -619,7 +619,7 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
} }
QList< Calamares::job_ptr > Calamares::JobList
PartitionViewStep::jobs() const PartitionViewStep::jobs() const
{ {
return m_core->jobs(); return m_core->jobs();
@ -638,7 +638,11 @@ PartitionViewStep::checkRequirements()
[]{ return tr( "has at least one disk device available." ); }, []{ return tr( "has at least one disk device available." ); },
[]{ return tr( "There are no partitons to install on." ); }, []{ return tr( "There are no partitons to install on." ); },
m_core->deviceModel()->rowCount() > 0, // satisfied m_core->deviceModel()->rowCount() > 0, // satisfied
true // required #ifdef DEBUG_PARTITION_UNSAFE
false // optional
#else
true // required
#endif
} ); } );
return l; return l;

View File

@ -69,7 +69,7 @@ public:
void setConfigurationMap( const QVariantMap& configurationMap ) override; void setConfigurationMap( const QVariantMap& configurationMap ) override;
QList< Calamares::job_ptr > jobs() const override; Calamares::JobList jobs() const override;
Calamares::RequirementsList checkRequirements() override; Calamares::RequirementsList checkRequirements() override;

View File

@ -270,6 +270,12 @@ WelcomePage::externallySelectedLanguage( int row )
ui->languageWidget->setCurrentIndex( row ); ui->languageWidget->setCurrentIndex( row );
} }
void
WelcomePage::setLanguageIcon( QPixmap i )
{
ui->languageIcon->setPixmap( i );
}
void void
LocaleTwoColumnDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const LocaleTwoColumnDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const

View File

@ -42,6 +42,8 @@ public:
void setUpLinks( bool showSupportUrl, void setUpLinks( bool showSupportUrl,
bool showKnownIssuesUrl, bool showKnownIssuesUrl,
bool showReleaseNotesUrl ); bool showReleaseNotesUrl );
/// @brief Set international language-selector icon
void setLanguageIcon( QPixmap );
/// @brief Results of requirements checking /// @brief Results of requirements checking
bool verdict() const; bool verdict() const;

View File

@ -67,7 +67,7 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="languageIcon">
<property name="toolTip"> <property name="toolTip">
<string>Select language</string> <string>Select language</string>
</property> </property>

View File

@ -24,10 +24,12 @@
#include "geoip/Handler.h" #include "geoip/Handler.h"
#include "locale/Lookup.h" #include "locale/Lookup.h"
#include "modulesystem/ModuleManager.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Variant.h" #include "utils/Variant.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.h"
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QVariant> #include <QVariant>
@ -137,6 +139,15 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap )
} ); } );
future->setFuture( handler->queryRaw() ); future->setFuture( handler->queryRaw() );
} }
QString language = CalamaresUtils::getString( configurationMap, "languageIcon" );
if ( !language.isEmpty() )
{
auto icon = Calamares::Branding::instance()->image( language, QSize( 48, 48 ) );
if ( !icon.isNull() )
m_widget->setLanguageIcon( icon );
}
} }
Calamares::RequirementsList Calamares::RequirementsList

View File

@ -64,3 +64,16 @@ geoip:
style: "none" style: "none"
url: "https://geoip.kde.org/v1/ubiquity" # extended XML format url: "https://geoip.kde.org/v1/ubiquity" # extended XML format
selector: "CountryCode" # blank uses default, which is wrong selector: "CountryCode" # blank uses default, which is wrong
# User interface
#
# The "select language" icon is an international standard, but it
# might not theme very well with your desktop environment.
# Fill in an icon name (following FreeDesktop standards) to
# use that named icon instead of the usual one.
#
# Leave blank or unset to use the international standard.
#
# Known icons in this space are "set-language" and "config-language".
#
# languageIcon: set-language