Merge branch 'pr-1632' into work-3.3
- merge in recent *calamares* branch FIXES #1632 (PR from Anubhav) FIXES #1886 FIXES #1456 FIXES #517
This commit is contained in:
commit
e15e57600e
16
CHANGES-3.2
16
CHANGES-3.2
@ -8,6 +8,22 @@ contributors are listed. Note that Calamares does not have a historical
|
|||||||
changelog -- this log starts with version 3.2.0. The release notes on the
|
changelog -- this log starts with version 3.2.0. The release notes on the
|
||||||
website will have to do for older versions.
|
website will have to do for older versions.
|
||||||
|
|
||||||
|
# 3.2.57 (unreleased) #
|
||||||
|
|
||||||
|
This release contains contributions from (alphabetically by first name):
|
||||||
|
- Arjen Balfoort (new contributor! Welcome!)
|
||||||
|
- Victor Fuentes
|
||||||
|
|
||||||
|
## Core ##
|
||||||
|
- No core changes yet
|
||||||
|
|
||||||
|
## Modules ##
|
||||||
|
- *fstab* and *luksbootkeyfile* have better support for an **un**encrypted
|
||||||
|
`/boot` partition. #1931 (thanks Arjen)
|
||||||
|
- *packagechooser* and *packagechooserq* can now be given a custom name
|
||||||
|
in the side-panel. #1932 (thanks Victor)
|
||||||
|
|
||||||
|
|
||||||
# 3.2.56 (2022-04-22) #
|
# 3.2.56 (2022-04-22) #
|
||||||
|
|
||||||
As of this release, Calamares 3.2 development is winding down. The
|
As of this release, Calamares 3.2 development is winding down. The
|
||||||
|
@ -798,8 +798,12 @@ def run():
|
|||||||
|
|
||||||
fw_type = libcalamares.globalstorage.value("firmwareType")
|
fw_type = libcalamares.globalstorage.value("firmwareType")
|
||||||
|
|
||||||
if (libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi"):
|
if libcalamares.globalstorage.value("bootLoader") is None:
|
||||||
|
# Don't want a bootloader, but do log that this has an effect:
|
||||||
|
if fw_type != "efi":
|
||||||
libcalamares.utils.warning( "Non-EFI system, and no bootloader is set." )
|
libcalamares.utils.warning( "Non-EFI system, and no bootloader is set." )
|
||||||
|
else:
|
||||||
|
libcalamares.utils.warning( "EFI system, and no bootloader is set." )
|
||||||
return None
|
return None
|
||||||
|
|
||||||
partitions = libcalamares.globalstorage.value("partitions")
|
partitions = libcalamares.globalstorage.value("partitions")
|
||||||
|
32
src/modules/fstab/main.py
Normal file → Executable file
32
src/modules/fstab/main.py
Normal file → Executable file
@ -156,11 +156,23 @@ class FstabGenerator(object):
|
|||||||
if not mapper_name or not luks_uuid:
|
if not mapper_name or not luks_uuid:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
password = "/crypto_keyfile.bin"
|
||||||
|
crypttab_options = self.crypttab_options
|
||||||
|
|
||||||
|
# Set crypttab password for partition to none and remove crypttab options
|
||||||
|
# on root partition when /boot is unencrypted
|
||||||
|
if partition["mountPoint"] == "/":
|
||||||
|
if any([p["mountPoint"] == "/boot"
|
||||||
|
and "luksMapperName" not in p
|
||||||
|
for p in self.partitions]):
|
||||||
|
password = "none"
|
||||||
|
crypttab_options = ""
|
||||||
|
|
||||||
return dict(
|
return dict(
|
||||||
name=mapper_name,
|
name=mapper_name,
|
||||||
device="UUID=" + luks_uuid,
|
device="UUID=" + luks_uuid,
|
||||||
password="/crypto_keyfile.bin",
|
password=password,
|
||||||
options=self.crypttab_options,
|
options=crypttab_options,
|
||||||
)
|
)
|
||||||
|
|
||||||
def print_crypttab_line(self, dct, file=None):
|
def print_crypttab_line(self, dct, file=None):
|
||||||
@ -218,7 +230,7 @@ class FstabGenerator(object):
|
|||||||
# Some "fs" names need special handling in /etc/fstab, so remap them.
|
# Some "fs" names need special handling in /etc/fstab, so remap them.
|
||||||
filesystem = partition["fs"].lower()
|
filesystem = partition["fs"].lower()
|
||||||
filesystem = FS_MAP.get(filesystem, filesystem)
|
filesystem = FS_MAP.get(filesystem, filesystem)
|
||||||
has_luks = "luksMapperName" in partition
|
luks_mapper_name = partition.get("luksMapperName", None)
|
||||||
mount_point = partition["mountPoint"]
|
mount_point = partition["mountPoint"]
|
||||||
disk_name = disk_name_for_partition(partition)
|
disk_name = disk_name_for_partition(partition)
|
||||||
is_ssd = disk_name in self.ssd_disks
|
is_ssd = disk_name in self.ssd_disks
|
||||||
@ -247,13 +259,23 @@ class FstabGenerator(object):
|
|||||||
if mount_point == "/":
|
if mount_point == "/":
|
||||||
self.root_is_ssd = is_ssd
|
self.root_is_ssd = is_ssd
|
||||||
|
|
||||||
if has_luks:
|
# If there's a set-and-not-empty subvolume set, add it
|
||||||
device = "/dev/mapper/" + partition["luksMapperName"]
|
if filesystem == "btrfs" and partition.get("subvol",None):
|
||||||
|
options = "subvol={},".format(partition["subvol"]) + options
|
||||||
|
|
||||||
|
device = None
|
||||||
|
if luks_mapper_name:
|
||||||
|
device = "/dev/mapper/" + luks_mapper_name
|
||||||
elif partition["uuid"]:
|
elif partition["uuid"]:
|
||||||
device = "UUID=" + partition["uuid"]
|
device = "UUID=" + partition["uuid"]
|
||||||
else:
|
else:
|
||||||
device = partition["device"]
|
device = partition["device"]
|
||||||
|
|
||||||
|
if not device:
|
||||||
|
# TODO: we get here when the user mounted a previously encrypted partition
|
||||||
|
# This should be catched early in the process
|
||||||
|
return None
|
||||||
|
|
||||||
return dict(device=device,
|
return dict(device=device,
|
||||||
mount_point=mount_point,
|
mount_point=mount_point,
|
||||||
fs=filesystem,
|
fs=filesystem,
|
||||||
|
@ -11,3 +11,10 @@ calamares_add_plugin(luksbootkeyfile
|
|||||||
SHARED_LIB
|
SHARED_LIB
|
||||||
NO_CONFIG
|
NO_CONFIG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
calamares_add_test(
|
||||||
|
luksbootkeyfiletest
|
||||||
|
SOURCES
|
||||||
|
Tests.cpp
|
||||||
|
LuksBootKeyFileJob.cpp
|
||||||
|
)
|
||||||
|
@ -150,26 +150,50 @@ setupLuks( const LuksDevice& d )
|
|||||||
}
|
}
|
||||||
|
|
||||||
static QVariantList
|
static QVariantList
|
||||||
partitions()
|
partitionsFromGlobalStorage()
|
||||||
{
|
{
|
||||||
Calamares::GlobalStorage* globalStorage = Calamares::JobQueue::instance()->globalStorage();
|
Calamares::GlobalStorage* globalStorage = Calamares::JobQueue::instance()->globalStorage();
|
||||||
return globalStorage->value( QStringLiteral( "partitions" ) ).toList();
|
return globalStorage->value( QStringLiteral( "partitions" ) ).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
/// Checks if the partition (represented by @p map) mounts to the given @p path
|
||||||
|
STATICTEST bool
|
||||||
|
hasMountPoint( const QVariantMap& map, const QString& path )
|
||||||
|
{
|
||||||
|
const auto v = map.value( QStringLiteral( "mountPoint" ) );
|
||||||
|
return v.isValid() && QDir::cleanPath( v.toString() ) == path;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATICTEST bool
|
||||||
|
isEncrypted( const QVariantMap& map )
|
||||||
|
{
|
||||||
|
return map.contains( QStringLiteral( "luksMapperName" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks for any partition satisfying @p pred
|
||||||
|
STATICTEST bool
|
||||||
|
anyPartition( bool ( *pred )( const QVariantMap& ) )
|
||||||
|
{
|
||||||
|
const auto partitions = partitionsFromGlobalStorage();
|
||||||
|
return std::find_if( partitions.cbegin(),
|
||||||
|
partitions.cend(),
|
||||||
|
[ &pred ]( const QVariant& partitionVariant ) { return pred( partitionVariant.toMap() ); } )
|
||||||
|
!= partitions.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
STATICTEST bool
|
||||||
hasUnencryptedSeparateBoot()
|
hasUnencryptedSeparateBoot()
|
||||||
{
|
{
|
||||||
const QVariantList partitions = ::partitions();
|
return anyPartition(
|
||||||
for ( const QVariant& partition : partitions )
|
[]( const QVariantMap& partition )
|
||||||
{
|
{ return hasMountPoint( partition, QStringLiteral( "/boot" ) ) && !isEncrypted( partition ); } );
|
||||||
QVariantMap partitionMap = partition.toMap();
|
}
|
||||||
QString mountPoint = partitionMap.value( QStringLiteral( "mountPoint" ) ).toString();
|
|
||||||
if ( QDir::cleanPath( mountPoint ) == QStringLiteral( "/boot" ) )
|
STATICTEST bool
|
||||||
{
|
hasEncryptedRoot()
|
||||||
return !partitionMap.contains( QStringLiteral( "luksMapperName" ) );
|
{
|
||||||
}
|
return anyPartition( []( const QVariantMap& partition )
|
||||||
}
|
{ return hasMountPoint( partition, QStringLiteral( "/" ) ) && isEncrypted( partition ); } );
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Calamares::JobResult
|
Calamares::JobResult
|
||||||
@ -218,7 +242,8 @@ LuksBootKeyFileJob::exec()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// /boot partition is not encrypted, keyfile must not be used
|
// /boot partition is not encrypted, keyfile must not be used
|
||||||
if ( hasUnencryptedSeparateBoot() )
|
// But only if root partition is not encrypted
|
||||||
|
if ( hasUnencryptedSeparateBoot() && !hasEncryptedRoot() )
|
||||||
{
|
{
|
||||||
cDebug() << Logger::SubEntry << "/boot partition is not encrypted, skipping keyfile creation.";
|
cDebug() << Logger::SubEntry << "/boot partition is not encrypted, skipping keyfile creation.";
|
||||||
return Calamares::JobResult::ok();
|
return Calamares::JobResult::ok();
|
||||||
@ -241,6 +266,12 @@ LuksBootKeyFileJob::exec()
|
|||||||
|
|
||||||
for ( const auto& d : s.devices )
|
for ( const auto& d : s.devices )
|
||||||
{
|
{
|
||||||
|
// Skip setupLuks for root partition if system has an unencrypted /boot
|
||||||
|
if ( d.isRoot && hasUnencryptedSeparateBoot() )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ( !setupLuks( d ) )
|
if ( !setupLuks( d ) )
|
||||||
return Calamares::JobResult::error(
|
return Calamares::JobResult::error(
|
||||||
tr( "Encrypted rootfs setup error" ),
|
tr( "Encrypted rootfs setup error" ),
|
||||||
|
169
src/modules/luksbootkeyfile/Tests.cpp
Normal file
169
src/modules/luksbootkeyfile/Tests.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
#undef STATICTEST
|
||||||
|
#define STATICTEST extern
|
||||||
|
|
||||||
|
// Implementation details
|
||||||
|
STATICTEST bool hasMountPoint( const QVariantMap& map, const QString& path );
|
||||||
|
|
||||||
|
STATICTEST bool isEncrypted( const QVariantMap& map );
|
||||||
|
|
||||||
|
STATICTEST bool anyPartition( bool ( *pred )( const QVariantMap& ) );
|
||||||
|
|
||||||
|
STATICTEST bool hasUnencryptedSeparateBoot();
|
||||||
|
|
||||||
|
STATICTEST bool hasEncryptedRoot();
|
||||||
|
|
||||||
|
class LuksBootKeyFileTests : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
LuksBootKeyFileTests() {}
|
||||||
|
~LuksBootKeyFileTests() override {}
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void initTestCase();
|
||||||
|
|
||||||
|
void testMountPoint();
|
||||||
|
void testIsEncrypted();
|
||||||
|
void testAnyPartition();
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
LuksBootKeyFileTests::initTestCase()
|
||||||
|
{
|
||||||
|
Logger::setupLogLevel( Logger::LOGDEBUG );
|
||||||
|
cDebug() << "LuksBootKeyFile test started.";
|
||||||
|
|
||||||
|
if ( !Calamares::JobQueue::instance() )
|
||||||
|
{
|
||||||
|
(void)new Calamares::JobQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LuksBootKeyFileTests::testMountPoint()
|
||||||
|
{
|
||||||
|
QVariantMap m; // As if this is a partition data
|
||||||
|
const QString key = QStringLiteral( "mountPoint" );
|
||||||
|
const QString boot = QStringLiteral( "/boot" );
|
||||||
|
const QString root = QStringLiteral( "/" );
|
||||||
|
|
||||||
|
QVERIFY( !hasMountPoint( m, QString() ) );
|
||||||
|
QVERIFY( !hasMountPoint( m, boot ) );
|
||||||
|
|
||||||
|
m.insert( key, boot );
|
||||||
|
QVERIFY( hasMountPoint( m, boot ) );
|
||||||
|
QVERIFY( !hasMountPoint( m, QString() ) );
|
||||||
|
QVERIFY( !hasMountPoint( m, root ) );
|
||||||
|
|
||||||
|
m.insert( key, root );
|
||||||
|
QVERIFY( !hasMountPoint( m, boot ) );
|
||||||
|
QVERIFY( !hasMountPoint( m, QString() ) );
|
||||||
|
QVERIFY( hasMountPoint( m, root ) );
|
||||||
|
|
||||||
|
m.remove( key );
|
||||||
|
QVERIFY( !hasMountPoint( m, root ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LuksBootKeyFileTests::testIsEncrypted()
|
||||||
|
{
|
||||||
|
QVariantMap m; // As if this is a partition data
|
||||||
|
const QString key = QStringLiteral( "luksMapperName" );
|
||||||
|
const QString name = QStringLiteral( "any-name" );
|
||||||
|
|
||||||
|
QVERIFY( !isEncrypted( m ) );
|
||||||
|
|
||||||
|
// Even an empty string is considered encrypted
|
||||||
|
m.insert( key, QString() );
|
||||||
|
QVERIFY( isEncrypted( m ) );
|
||||||
|
|
||||||
|
m.insert( key, name );
|
||||||
|
QVERIFY( isEncrypted( m ) );
|
||||||
|
|
||||||
|
m.insert( key, QString() );
|
||||||
|
QVERIFY( isEncrypted( m ) );
|
||||||
|
|
||||||
|
m.remove( key );
|
||||||
|
QVERIFY( !isEncrypted( m ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
LuksBootKeyFileTests::testAnyPartition()
|
||||||
|
{
|
||||||
|
// This is kind of annoying: we need to build up
|
||||||
|
// partition data in GS because the functions we're testing
|
||||||
|
// go straight to GS.
|
||||||
|
auto* gs = Calamares::JobQueue::instanceGlobalStorage();
|
||||||
|
QVERIFY( gs );
|
||||||
|
|
||||||
|
const QString partitionsKey = QStringLiteral( "partitions" );
|
||||||
|
const QString mountPointKey = QStringLiteral( "mountPoint" );
|
||||||
|
const QString boot = QStringLiteral( "/boot" );
|
||||||
|
const QString root = QStringLiteral( "/" );
|
||||||
|
|
||||||
|
QVariantList partitions;
|
||||||
|
QVariantMap p;
|
||||||
|
QVERIFY( !gs->contains( partitionsKey ) );
|
||||||
|
|
||||||
|
// Empty list!
|
||||||
|
QVERIFY( !anyPartition( []( const QVariantMap& ) { return true; } ) );
|
||||||
|
|
||||||
|
gs->insert( partitionsKey, partitions );
|
||||||
|
QVERIFY( !anyPartition( []( const QVariantMap& ) { return true; } ) ); // Still an empty list
|
||||||
|
|
||||||
|
partitions.append( p );
|
||||||
|
QCOMPARE( partitions.count(), 1 );
|
||||||
|
gs->insert( partitionsKey, partitions );
|
||||||
|
QVERIFY( anyPartition( []( const QVariantMap& ) { return true; } ) ); // Now a one-element list
|
||||||
|
QVERIFY( !anyPartition( []( const QVariantMap& ) { return false; } ) ); // Now a one-element list
|
||||||
|
|
||||||
|
p.insert( mountPointKey, boot );
|
||||||
|
QVERIFY( hasMountPoint( p, boot ) );
|
||||||
|
partitions.append( p );
|
||||||
|
QCOMPARE( partitions.count(), 2 );
|
||||||
|
|
||||||
|
// Note that GS is not updated yet, so we expect this to fail
|
||||||
|
QEXPECT_FAIL( "", "GS not updated", Continue );
|
||||||
|
QVERIFY( anyPartition(
|
||||||
|
[]( const QVariantMap& partdata )
|
||||||
|
{
|
||||||
|
cDebug() << partdata;
|
||||||
|
return hasMountPoint( partdata, QStringLiteral( "/boot" ) );
|
||||||
|
} ) );
|
||||||
|
|
||||||
|
gs->insert( partitionsKey, partitions ); // Update GS
|
||||||
|
QVERIFY( anyPartition(
|
||||||
|
[]( const QVariantMap& partdata )
|
||||||
|
{
|
||||||
|
cDebug() << partdata;
|
||||||
|
return hasMountPoint( partdata, QStringLiteral( "/boot" ) );
|
||||||
|
} ) );
|
||||||
|
QVERIFY( !anyPartition( []( const QVariantMap& partdata )
|
||||||
|
{ return hasMountPoint( partdata, QStringLiteral( "/" ) ); } ) );
|
||||||
|
QVERIFY( !anyPartition( []( const QVariantMap& partdata ) { return hasMountPoint( partdata, QString() ); } ) );
|
||||||
|
|
||||||
|
QVERIFY( !hasEncryptedRoot() );
|
||||||
|
QVERIFY( hasUnencryptedSeparateBoot() );
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN( LuksBootKeyFileTests )
|
||||||
|
|
||||||
|
#include "utils/moc-warnings.h"
|
||||||
|
|
||||||
|
#include "Tests.moc"
|
@ -237,6 +237,12 @@ Config::setPackageChoice( const QString& packageChoice )
|
|||||||
emit packageChoiceChanged( m_packageChoice.value_or( QString() ) );
|
emit packageChoiceChanged( m_packageChoice.value_or( QString() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
Config::prettyName() const
|
||||||
|
{
|
||||||
|
return m_stepName ? m_stepName->get() : tr( "Packages" );
|
||||||
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
Config::prettyStatus() const
|
Config::prettyStatus() const
|
||||||
{
|
{
|
||||||
@ -343,4 +349,14 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
|||||||
cWarning() << "Single-selection QML module must use 'Legacy' method.";
|
cWarning() << "Single-selection QML module must use 'Legacy' method.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool labels_ok = false;
|
||||||
|
auto labels = CalamaresUtils::getSubMap( configurationMap, "labels", labels_ok );
|
||||||
|
if ( labels_ok )
|
||||||
|
{
|
||||||
|
if ( labels.contains( "step" ) )
|
||||||
|
{
|
||||||
|
m_stepName = new CalamaresUtils::Locale::TranslatedString( labels, "step" );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,7 @@ public:
|
|||||||
QString packageChoice() const { return m_packageChoice.value_or( QString() ); }
|
QString packageChoice() const { return m_packageChoice.value_or( QString() ); }
|
||||||
void setPackageChoice( const QString& packageChoice );
|
void setPackageChoice( const QString& packageChoice );
|
||||||
|
|
||||||
|
QString prettyName() const;
|
||||||
QString prettyStatus() const;
|
QString prettyStatus() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -120,6 +121,7 @@ private:
|
|||||||
* Reading the property will return an empty QString.
|
* Reading the property will return an empty QString.
|
||||||
*/
|
*/
|
||||||
std::optional< QString > m_packageChoice;
|
std::optional< QString > m_packageChoice;
|
||||||
|
CalamaresUtils::Locale::TranslatedString* m_stepName; // As it appears in the sidebar
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ PackageChooserViewStep::PackageChooserViewStep( QObject* parent )
|
|||||||
: Calamares::ViewStep( parent )
|
: Calamares::ViewStep( parent )
|
||||||
, m_config( new Config( this ) )
|
, m_config( new Config( this ) )
|
||||||
, m_widget( nullptr )
|
, m_widget( nullptr )
|
||||||
, m_stepName( nullptr )
|
|
||||||
{
|
{
|
||||||
emit nextStatusChanged( false );
|
emit nextStatusChanged( false );
|
||||||
}
|
}
|
||||||
@ -41,14 +40,13 @@ PackageChooserViewStep::~PackageChooserViewStep()
|
|||||||
{
|
{
|
||||||
m_widget->deleteLater();
|
m_widget->deleteLater();
|
||||||
}
|
}
|
||||||
delete m_stepName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString
|
QString
|
||||||
PackageChooserViewStep::prettyName() const
|
PackageChooserViewStep::prettyName() const
|
||||||
{
|
{
|
||||||
return m_stepName ? m_stepName->get() : tr( "Packages" );
|
return m_config->prettyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -139,16 +137,6 @@ PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap
|
|||||||
m_config->setDefaultId( moduleInstanceKey() );
|
m_config->setDefaultId( moduleInstanceKey() );
|
||||||
m_config->setConfigurationMap( configurationMap );
|
m_config->setConfigurationMap( configurationMap );
|
||||||
|
|
||||||
bool labels_ok = false;
|
|
||||||
auto labels = CalamaresUtils::getSubMap( configurationMap, "labels", labels_ok );
|
|
||||||
if ( labels_ok )
|
|
||||||
{
|
|
||||||
if ( labels.contains( "step" ) )
|
|
||||||
{
|
|
||||||
m_stepName = new CalamaresUtils::Locale::TranslatedString( labels, "step" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( m_widget )
|
if ( m_widget )
|
||||||
{
|
{
|
||||||
hookupModel();
|
hookupModel();
|
||||||
|
@ -50,7 +50,6 @@ private:
|
|||||||
|
|
||||||
Config* m_config;
|
Config* m_config;
|
||||||
PackageChooserPage* m_widget;
|
PackageChooserPage* m_widget;
|
||||||
CalamaresUtils::Locale::TranslatedString* m_stepName; // As it appears in the sidebar
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory )
|
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory )
|
||||||
|
@ -29,7 +29,7 @@ PackageChooserQmlViewStep::PackageChooserQmlViewStep( QObject* parent )
|
|||||||
QString
|
QString
|
||||||
PackageChooserQmlViewStep::prettyName() const
|
PackageChooserQmlViewStep::prettyName() const
|
||||||
{
|
{
|
||||||
return tr( "Packages" );
|
return m_config->prettyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
|
@ -42,6 +42,19 @@
|
|||||||
#
|
#
|
||||||
method: legacy
|
method: legacy
|
||||||
|
|
||||||
|
# Human-visible strings in this module. These are all optional.
|
||||||
|
# The following translated keys are used:
|
||||||
|
# - *step*, used in the overall progress view (left-hand pane)
|
||||||
|
#
|
||||||
|
# Each key can have a [locale] added to it, which is used as
|
||||||
|
# the translated string for that locale. For the strings
|
||||||
|
# associated with the "no-selection" item, see *items*, below
|
||||||
|
# with the explicit item-*id* "".
|
||||||
|
#
|
||||||
|
labels:
|
||||||
|
step: "Packages"
|
||||||
|
step[nl]: "Pakketten"
|
||||||
|
|
||||||
# The *packageChoice* value is used for setting the default selection
|
# The *packageChoice* value is used for setting the default selection
|
||||||
# in the QML view; this should match one of the keys used in the QML
|
# in the QML view; this should match one of the keys used in the QML
|
||||||
# module for package names.
|
# module for package names.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
* SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Anubhav Choudhary <ac.10edu@gmail.com>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
*
|
||||||
* Calamares is Free Software: see the License-Identifier above.
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
@ -79,7 +80,7 @@ BootLoaderModel::updateInternal()
|
|||||||
clear();
|
clear();
|
||||||
createMbrItems();
|
createMbrItems();
|
||||||
|
|
||||||
// An empty model is possible if you don't havee permissions: don't crash though.
|
// An empty model is possible if you don't have permissions: don't crash though.
|
||||||
if ( rowCount() < 1 )
|
if ( rowCount() < 1 )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -124,10 +125,10 @@ BootLoaderModel::updateInternal()
|
|||||||
{
|
{
|
||||||
appendRow( createBootLoaderItem( partitionText, PartitionInfo::mountPoint( partition ), true ) );
|
appendRow( createBootLoaderItem( partitionText, PartitionInfo::mountPoint( partition ), true ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create "don't install bootloader" item
|
|
||||||
appendRow( createBootLoaderItem( tr( "Do not install a boot loader" ), QString(), false ) );
|
|
||||||
}
|
}
|
||||||
|
// Create "don't install bootloader" item. This is always available,
|
||||||
|
// also if there was no /boot or / partition found.
|
||||||
|
appendRow( createBootLoaderItem( tr( "Do not install a boot loader" ), QString(), false ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
* SPDX-FileCopyrightText: 2014-2017 Teo Mrnjavac <teo@kde.org>
|
* SPDX-FileCopyrightText: 2014-2017 Teo Mrnjavac <teo@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
|
* SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
|
||||||
* SPDX-FileCopyrightText: 2019 Collabora Ltd
|
* SPDX-FileCopyrightText: 2019 Collabora Ltd
|
||||||
|
* SPDX-FileCopyrightText: 2021 Anubhav Choudhary <ac.10edu@gmail.com>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
*
|
||||||
* Calamares is Free Software: see the License-Identifier above.
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
@ -1021,6 +1022,12 @@ ChoicePage::updateActionChoicePreview( 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 );
|
||||||
|
|
||||||
|
if ( !m_isEfi )
|
||||||
|
{
|
||||||
|
layout->addWidget( createBootloaderPanel() );
|
||||||
|
}
|
||||||
|
|
||||||
connect( m_afterPartitionSplitterWidget,
|
connect( m_afterPartitionSplitterWidget,
|
||||||
&PartitionSplitterWidget::partitionResized,
|
&PartitionSplitterWidget::partitionResized,
|
||||||
this,
|
this,
|
||||||
@ -1079,51 +1086,7 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
|
|||||||
|
|
||||||
if ( !m_isEfi )
|
if ( !m_isEfi )
|
||||||
{
|
{
|
||||||
QWidget* eraseWidget = new QWidget;
|
layout->addWidget( createBootloaderPanel() );
|
||||||
|
|
||||||
QHBoxLayout* eraseLayout = new QHBoxLayout;
|
|
||||||
eraseWidget->setLayout( eraseLayout );
|
|
||||||
eraseLayout->setContentsMargins( 0, 0, 0, 0 );
|
|
||||||
QLabel* eraseBootloaderLabel = new QLabel( eraseWidget );
|
|
||||||
eraseLayout->addWidget( eraseBootloaderLabel );
|
|
||||||
eraseBootloaderLabel->setText( tr( "Boot loader location:" ) );
|
|
||||||
|
|
||||||
m_bootloaderComboBox = createBootloaderComboBox( eraseWidget );
|
|
||||||
connect( m_core->bootLoaderModel(),
|
|
||||||
&QAbstractItemModel::modelReset,
|
|
||||||
[ this ]()
|
|
||||||
{
|
|
||||||
if ( !m_bootloaderComboBox.isNull() )
|
|
||||||
{
|
|
||||||
Calamares::restoreSelectedBootLoader( *m_bootloaderComboBox,
|
|
||||||
m_core->bootLoaderInstallPath() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
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.
|
|
||||||
|
|
||||||
eraseLayout->addWidget( m_bootloaderComboBox );
|
|
||||||
eraseBootloaderLabel->setBuddy( m_bootloaderComboBox );
|
|
||||||
eraseLayout->addStretch();
|
|
||||||
|
|
||||||
layout->addWidget( eraseWidget );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_previewAfterFrame->show();
|
m_previewAfterFrame->show();
|
||||||
@ -1235,35 +1198,6 @@ ChoicePage::setupEfiSystemPartitionSelector()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QComboBox*
|
|
||||||
ChoicePage::createBootloaderComboBox( QWidget* parent )
|
|
||||||
{
|
|
||||||
QComboBox* comboForBootloader = new QComboBox( parent );
|
|
||||||
comboForBootloader->setModel( m_core->bootLoaderModel() );
|
|
||||||
|
|
||||||
// When the chosen bootloader device changes, we update the choice in the PCM
|
|
||||||
connect( comboForBootloader,
|
|
||||||
QOverload< int >::of( &QComboBox::currentIndexChanged ),
|
|
||||||
this,
|
|
||||||
[ this ]( int newIndex )
|
|
||||||
{
|
|
||||||
QComboBox* bootloaderCombo = qobject_cast< QComboBox* >( sender() );
|
|
||||||
if ( bootloaderCombo )
|
|
||||||
{
|
|
||||||
QVariant var = bootloaderCombo->itemData( newIndex, BootLoaderModel::BootLoaderPathRole );
|
|
||||||
if ( !var.isValid() )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_core->setBootLoaderInstallPath( var.toString() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
return comboForBootloader;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
force_uncheck( QButtonGroup* grp, PrettyRadioButton* button )
|
force_uncheck( QButtonGroup* grp, PrettyRadioButton* button )
|
||||||
{
|
{
|
||||||
@ -1733,3 +1667,72 @@ ChoicePage::setLastSelectedDeviceIndex( int index )
|
|||||||
m_lastSelectedDeviceIndex = index;
|
m_lastSelectedDeviceIndex = index;
|
||||||
m_drivesCombo->setCurrentIndex( m_lastSelectedDeviceIndex );
|
m_drivesCombo->setCurrentIndex( m_lastSelectedDeviceIndex );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget*
|
||||||
|
ChoicePage::createBootloaderPanel()
|
||||||
|
{
|
||||||
|
QWidget* panelWidget = new QWidget;
|
||||||
|
|
||||||
|
QHBoxLayout* mainLayout = new QHBoxLayout;
|
||||||
|
panelWidget->setLayout( mainLayout );
|
||||||
|
mainLayout->setContentsMargins( 0, 0, 0, 0 );
|
||||||
|
QLabel* widgetLabel = new QLabel( panelWidget );
|
||||||
|
mainLayout->addWidget( widgetLabel );
|
||||||
|
widgetLabel->setText( tr( "Boot loader location:" ) );
|
||||||
|
|
||||||
|
QComboBox* comboForBootloader = new QComboBox( panelWidget );
|
||||||
|
comboForBootloader->setModel( m_core->bootLoaderModel() );
|
||||||
|
|
||||||
|
// When the chosen bootloader device changes, we update the choice in the PCM
|
||||||
|
connect( comboForBootloader,
|
||||||
|
QOverload< int >::of( &QComboBox::currentIndexChanged ),
|
||||||
|
this,
|
||||||
|
[ this ]( int newIndex )
|
||||||
|
{
|
||||||
|
QComboBox* bootloaderCombo = qobject_cast< QComboBox* >( sender() );
|
||||||
|
if ( bootloaderCombo )
|
||||||
|
{
|
||||||
|
QVariant var = bootloaderCombo->itemData( newIndex, BootLoaderModel::BootLoaderPathRole );
|
||||||
|
if ( !var.isValid() )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_core->setBootLoaderInstallPath( var.toString() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
m_bootloaderComboBox = comboForBootloader;
|
||||||
|
|
||||||
|
connect( m_core->bootLoaderModel(),
|
||||||
|
&QAbstractItemModel::modelReset,
|
||||||
|
[ this ]()
|
||||||
|
{
|
||||||
|
if ( !m_bootloaderComboBox.isNull() )
|
||||||
|
{
|
||||||
|
Calamares::restoreSelectedBootLoader( *m_bootloaderComboBox, m_core->bootLoaderInstallPath() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
connect(
|
||||||
|
m_core,
|
||||||
|
&PartitionCoreModule::deviceReverted,
|
||||||
|
this,
|
||||||
|
[ this ]( Device* )
|
||||||
|
{
|
||||||
|
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.
|
||||||
|
|
||||||
|
mainLayout->addWidget( m_bootloaderComboBox );
|
||||||
|
widgetLabel->setBuddy( m_bootloaderComboBox );
|
||||||
|
mainLayout->addStretch();
|
||||||
|
|
||||||
|
return panelWidget;
|
||||||
|
}
|
||||||
|
@ -108,7 +108,12 @@ private:
|
|||||||
void updateNextEnabled();
|
void updateNextEnabled();
|
||||||
void setupChoices();
|
void setupChoices();
|
||||||
void checkInstallChoiceRadioButton( Config::InstallChoice choice ); ///< Sets the chosen button to "on"
|
void checkInstallChoiceRadioButton( Config::InstallChoice choice ); ///< Sets the chosen button to "on"
|
||||||
QComboBox* createBootloaderComboBox( QWidget* parentButton );
|
/** @brief Create a panel with "boot loader location:"
|
||||||
|
*
|
||||||
|
* Panel + dropdown and handling for model updates. Returns a pointer
|
||||||
|
* to the panel's widget.
|
||||||
|
*/
|
||||||
|
QWidget* createBootloaderPanel();
|
||||||
Device* selectedDevice();
|
Device* selectedDevice();
|
||||||
|
|
||||||
/* Change the UI depending on the device selected. */
|
/* Change the UI depending on the device selected. */
|
||||||
|
Loading…
Reference in New Issue
Block a user