Merge branch 'fix-swap-ui' of https://github.com/calamares/calamares into development

This commit is contained in:
Philip Müller 2018-10-15 21:16:38 +02:00
commit 7bd99940b8
16 changed files with 500 additions and 218 deletions

View File

@ -14,7 +14,14 @@ This release contains contributions from (alphabetically by first name):
## Core ## ## Core ##
There are no core changes in this version. * The Calamares application now recognizes the `-X` or `--xdg-config`
option, which adds XDG_DATA_DIRS to the places used to find QML
and branding directories, and XDG_CONFIG_DIRS to the places used
to find the global settings and module configurations. This allows
a more fine-grained, and more layered, approach to setting up
Calamares configurations (in particular, distro's can **add**
configuration files and give them priority, instead of **forking**
configuration files).
## Modules ## ## Modules ##

View File

@ -145,6 +145,9 @@ qmlDirCandidates( bool assumeBuilddir )
{ {
if ( assumeBuilddir ) if ( assumeBuilddir )
qmlDirs << QDir::current().absoluteFilePath( "src/qml" ); // In build-dir qmlDirs << QDir::current().absoluteFilePath( "src/qml" ); // In build-dir
if ( CalamaresUtils::haveExtraDirs() )
for ( auto s : CalamaresUtils::extraDataDirs() )
qmlDirs << ( s + QML );
qmlDirs << CalamaresUtils::appDataDir().absoluteFilePath( QML ); qmlDirs << CalamaresUtils::appDataDir().absoluteFilePath( QML );
} }
@ -164,6 +167,9 @@ settingsFileCandidates( bool assumeBuilddir )
{ {
if ( assumeBuilddir ) if ( assumeBuilddir )
settingsPaths << QDir::current().absoluteFilePath( settings ); settingsPaths << QDir::current().absoluteFilePath( settings );
if ( CalamaresUtils::haveExtraDirs() )
for ( auto s : CalamaresUtils::extraConfigDirs() )
settingsPaths << ( s + settings );
settingsPaths << CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/settings.conf"; // String concat settingsPaths << CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/settings.conf"; // String concat
settingsPaths << CalamaresUtils::appDataDir().absoluteFilePath( settings ); settingsPaths << CalamaresUtils::appDataDir().absoluteFilePath( settings );
} }
@ -182,6 +188,9 @@ brandingFileCandidates( bool assumeBuilddir, const QString& brandingFilename )
{ {
if ( assumeBuilddir ) if ( assumeBuilddir )
brandingPaths << ( QDir::currentPath() + QStringLiteral( "/src/" ) + brandingFilename ); brandingPaths << ( QDir::currentPath() + QStringLiteral( "/src/" ) + brandingFilename );
if ( CalamaresUtils::haveExtraDirs() )
for ( auto s : CalamaresUtils::extraDataDirs() )
brandingPaths << ( s + brandingFilename );
brandingPaths << QDir( CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/" ).absoluteFilePath( brandingFilename ); brandingPaths << QDir( CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/" ).absoluteFilePath( brandingFilename );
brandingPaths << CalamaresUtils::appDataDir().absoluteFilePath( brandingFilename); brandingPaths << CalamaresUtils::appDataDir().absoluteFilePath( brandingFilename);
} }

View File

@ -44,6 +44,8 @@ handle_args( CalamaresApplication& a )
"Verbose output for debugging purposes (0-8).", "level" ); "Verbose output for debugging purposes (0-8).", "level" );
QCommandLineOption configOption( QStringList{ "c", "config"}, QCommandLineOption configOption( QStringList{ "c", "config"},
"Configuration directory to use, for testing purposes.", "config" ); "Configuration directory to use, for testing purposes.", "config" );
QCommandLineOption xdgOption( QStringList{"X", "xdg-config"},
"Use XDG_{CONFIG,DATA}_DIRS as well." );
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription( "Distribution-independent installer framework" ); parser.setApplicationDescription( "Distribution-independent installer framework" );
@ -53,6 +55,7 @@ handle_args( CalamaresApplication& a )
parser.addOption( debugOption ); parser.addOption( debugOption );
parser.addOption( debugLevelOption ); parser.addOption( debugLevelOption );
parser.addOption( configOption ); parser.addOption( configOption );
parser.addOption( xdgOption );
parser.process( a ); parser.process( a );
@ -72,6 +75,8 @@ handle_args( CalamaresApplication& a )
} }
if ( parser.isSet( configOption ) ) if ( parser.isSet( configOption ) )
CalamaresUtils::setAppDataDir( QDir( parser.value( configOption ) ) ); CalamaresUtils::setAppDataDir( QDir( parser.value( configOption ) ) );
if ( parser.isSet( xdgOption ) )
CalamaresUtils::setXdgDirs();
} }
int int

View File

@ -42,7 +42,7 @@ LibCalamaresTests::testDebugLevels()
{ {
Logger::setupLogLevel( Logger::LOG_DISABLE ); Logger::setupLogLevel( Logger::LOG_DISABLE );
QCOMPARE( Logger::logLevel(), Logger::LOG_DISABLE ); QCOMPARE( Logger::logLevel(), static_cast<unsigned int>( Logger::LOG_DISABLE ) );
for ( unsigned int level = 0; level <= Logger::LOGVERBOSE ; ++level ) for ( unsigned int level = 0; level <= Logger::LOGVERBOSE ; ++level )
{ {

View File

@ -49,6 +49,9 @@ static QTranslator* s_brandingTranslator = nullptr;
static QTranslator* s_translator = nullptr; static QTranslator* s_translator = nullptr;
static QString s_translatorLocaleName; static QString s_translatorLocaleName;
static bool s_haveExtraDirs = false;
static QStringList s_extraConfigDirs;
static QStringList s_extraDataDirs;
static bool static bool
isWritableDir( const QDir& dir ) isWritableDir( const QDir& dir )
@ -94,6 +97,46 @@ setAppDataDir( const QDir& dir )
s_isAppDataDirOverridden = true; s_isAppDataDirOverridden = true;
} }
/* Split $ENV{@p name} on :, append to @p l, making sure each ends in / */
static void
mungeEnvironment( QStringList& l, const char *name )
{
for ( auto s : QString( qgetenv( name ) ).split(':') )
if ( s.endsWith( '/' ) )
l << s;
else
l << ( s + '/' );
}
void
setXdgDirs()
{
s_haveExtraDirs = true;
mungeEnvironment( s_extraConfigDirs, "XDG_CONFIG_DIRS" );
mungeEnvironment( s_extraDataDirs, "XDG_DATA_DIRS" );
}
QStringList
extraConfigDirs()
{
if ( s_haveExtraDirs )
return s_extraConfigDirs;
return QStringList();
}
QStringList
extraDataDirs()
{
if ( s_haveExtraDirs )
return s_extraDataDirs;
return QStringList();
}
bool
haveExtraDirs()
{
return s_haveExtraDirs && ( !s_extraConfigDirs.isEmpty() || !s_extraDataDirs.isEmpty() );
}
bool bool
isAppDataDirOverridden() isAppDataDirOverridden()

View File

@ -79,6 +79,16 @@ namespace CalamaresUtils
DLLEXPORT void setQmlModulesDir( const QDir& dir ); DLLEXPORT void setQmlModulesDir( const QDir& dir );
/** @brief Setup extra config and data dirs from the XDG variables.
*/
DLLEXPORT void setXdgDirs();
/** @brief Are any extra directories configured? */
DLLEXPORT bool haveExtraDirs();
/** @brief XDG_CONFIG_DIRS, each guaranteed to end with / */
DLLEXPORT QStringList extraConfigDirs();
/** @brief XDG_DATA_DIRS, each guaranteed to end with / */
DLLEXPORT QStringList extraDataDirs();
/** /**
* @brief removeDiacritics replaces letters with diacritics and ligatures with * @brief removeDiacritics replaces letters with diacritics and ligatures with
* alternative forms and digraphs. * alternative forms and digraphs.

View File

@ -155,6 +155,10 @@ moduleConfigurationCandidates( bool assumeBuildDir, const QString& moduleName, c
if ( assumeBuildDir && configFileName.contains( '/' ) ) if ( assumeBuildDir && configFileName.contains( '/' ) )
paths << QDir().absoluteFilePath( configFileName ); paths << QDir().absoluteFilePath( configFileName );
if ( CalamaresUtils::haveExtraDirs() )
for ( auto s : CalamaresUtils::extraConfigDirs() )
paths << ( s + QString( "modules/%1" ).arg( configFileName ) );
paths << QString( "/etc/calamares/modules/%1" ).arg( configFileName ); paths << QString( "/etc/calamares/modules/%1" ).arg( configFileName );
paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) ); paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) );
} }

View File

@ -28,7 +28,6 @@
#include "utils/Units.h" #include "utils/Units.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "GlobalStorage.h"
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
@ -43,58 +42,38 @@ using CalamaresUtils::operator""_GiB;
using CalamaresUtils::operator""_MiB; using CalamaresUtils::operator""_MiB;
qint64 qint64
swapSuggestion( const qint64 availableSpaceB ) swapSuggestion( const qint64 availableSpaceB, Choices::SwapChoice swap )
{ {
/* If suspend-to-disk is demanded, then we always need enough if ( ( swap != Choices::SmallSwap ) && ( swap != Choices::FullSwap ) )
* swap to write the whole memory to disk -- between 2GB and 8GB return 0;
* RAM give proportionally more swap, and from 8GB RAM keep
* swap = RAM. // See partition.conf for explanation
*
* If suspend-to-disk is not demanded, then ramp up more slowly,
* to 8GB swap at 16GB memory, and then drop to 4GB for "large
* memory" machines, on the assumption that those don't need swap
* because they have tons of memory (or whatever they are doing,
* had better not run into swap).
*/
qint64 suggestedSwapSizeB = 0; qint64 suggestedSwapSizeB = 0;
auto memory = CalamaresUtils::System::instance()->getTotalMemoryB(); auto memory = CalamaresUtils::System::instance()->getTotalMemoryB();
qint64 availableRamB = memory.first; qint64 availableRamB = memory.first;
qreal overestimationFactor = memory.second; qreal overestimationFactor = memory.second;
bool ensureSuspendToDisk = bool ensureSuspendToDisk = swap == Choices::FullSwap;
Calamares::JobQueue::instance()->globalStorage()->
value( "ensureSuspendToDisk" ).toBool();
if ( ensureSuspendToDisk ) // Ramp up quickly to 8GiB, then follow memory size
{ if ( availableRamB <= 4_GiB )
if ( availableRamB < 4_GiB ) suggestedSwapSizeB = availableRamB * 2;
suggestedSwapSizeB = qMax( 2_GiB, availableRamB * 2 ); else if ( availableRamB <= 8_GiB )
else if ( availableRamB >= 4_GiB && availableRamB < 8_GiB )
suggestedSwapSizeB = 8_GiB; suggestedSwapSizeB = 8_GiB;
else else
suggestedSwapSizeB = availableRamB; suggestedSwapSizeB = availableRamB;
suggestedSwapSizeB *= overestimationFactor; // .. top out at 8GiB if we don't care about suspend
} if ( !ensureSuspendToDisk )
else //if we don't care about suspend to disk suggestedSwapSizeB = qMin( 8_GiB, suggestedSwapSizeB );
{
if ( availableRamB < 2_GiB )
suggestedSwapSizeB = qMax( 2_GiB, availableRamB * 2 );
else if ( availableRamB >= 2_GiB && availableRamB < 8_GiB )
suggestedSwapSizeB = availableRamB;
else if ( availableRamB >= 8_GiB && availableRamB < 16_GiB )
suggestedSwapSizeB = 8_GiB;
else
suggestedSwapSizeB = 4_GiB;
// Allow for a fudge factor
suggestedSwapSizeB *= overestimationFactor; suggestedSwapSizeB *= overestimationFactor;
// don't use more than 10% of available space // don't use more than 10% of available space
qreal maxSwapDiskRatio = 0.10; if ( !ensureSuspendToDisk )
qint64 maxSwapSizeB = availableSpaceB * maxSwapDiskRatio; suggestedSwapSizeB = qMin( suggestedSwapSizeB, qint64( 0.10 * availableSpaceB ) );
if ( suggestedSwapSizeB > maxSwapSizeB )
suggestedSwapSizeB = maxSwapSizeB;
}
cDebug() << "Suggested swap size:" << suggestedSwapSizeB / 1024. / 1024. / 1024. << "GiB"; cDebug() << "Suggested swap size:" << suggestedSwapSizeB / 1024. / 1024. / 1024. << "GiB";
@ -118,16 +97,14 @@ bytesToSectors( qint64 bytes, qint64 blocksize )
} }
void void
doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPassphrase ) doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionOptions o )
{ {
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); QString defaultFsType = o.defaultFsType;
bool isEfi = PartUtils::isEfiSystem();
QString defaultFsType = gs->value( "defaultFileSystemType" ).toString();
if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown ) if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown )
defaultFsType = "ext4"; defaultFsType = "ext4";
bool isEfi = PartUtils::isEfiSystem();
// Partition sizes are expressed in MiB, should be multiples of // Partition sizes are expressed in MiB, should be multiples of
// the logical sector size (usually 512B). EFI starts with 2MiB // the logical sector size (usually 512B). EFI starts with 2MiB
// empty and a 300MiB EFI boot partition, while BIOS starts at // empty and a 300MiB EFI boot partition, while BIOS starts at
@ -160,8 +137,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass
PartitionTable::FlagNone PartitionTable::FlagNone
); );
PartitionInfo::setFormat( efiPartition, true ); PartitionInfo::setFormat( efiPartition, true );
PartitionInfo::setMountPoint( efiPartition, gs->value( "efiSystemPartition" ) PartitionInfo::setMountPoint( efiPartition, o.efiPartitionMountPoint );
.toString() );
core->createPartition( dev, efiPartition, PartitionTable::FlagEsp ); core->createPartition( dev, efiPartition, PartitionTable::FlagEsp );
firstFreeSector = lastSector + 1; firstFreeSector = lastSector + 1;
} }
@ -170,20 +146,18 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass
core->createPartitionTable( dev, PartitionTable::msdos ); core->createPartitionTable( dev, PartitionTable::msdos );
} }
const bool mayCreateSwap = !gs->value( "neverCreateSwap" ).toBool(); const bool mayCreateSwap = ( o.swap == Choices::SmallSwap ) || ( o.swap == Choices::FullSwap );
bool shouldCreateSwap = false; bool shouldCreateSwap = false;
qint64 suggestedSwapSizeB = 0; qint64 suggestedSwapSizeB = 0;
if ( mayCreateSwap ) if ( mayCreateSwap )
{ {
qint64 availableSpaceB = ( dev->totalLogical() - firstFreeSector ) * dev->logicalSize(); qint64 availableSpaceB = ( dev->totalLogical() - firstFreeSector ) * dev->logicalSize();
suggestedSwapSizeB = swapSuggestion( availableSpaceB ); suggestedSwapSizeB = swapSuggestion( availableSpaceB, o.swap );
// Space required by this installation is what the distro claims is needed // Space required by this installation is what the distro claims is needed
// (via global configuration) plus the swap size plus a fudge factor of // (via global configuration) plus the swap size plus a fudge factor of
// 0.6GiB (this was 2.1GiB up to Calamares 3.2.2). // 0.6GiB (this was 2.1GiB up to Calamares 3.2.2).
qint64 requiredSpaceB = qint64 requiredSpaceB = o.requiredSpaceB + 600_MiB + suggestedSwapSizeB;
GiBtoBytes( gs->value( "requiredStorageGB" ).toDouble() + 0.6 ) +
suggestedSwapSizeB;
// If there is enough room for ESP + root + swap, create swap, otherwise don't. // If there is enough room for ESP + root + swap, create swap, otherwise don't.
shouldCreateSwap = availableSpaceB > requiredSpaceB; shouldCreateSwap = availableSpaceB > requiredSpaceB;
@ -196,7 +170,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass
} }
Partition* rootPartition = nullptr; Partition* rootPartition = nullptr;
if ( luksPassphrase.isEmpty() ) if ( o.luksPassphrase.isEmpty() )
{ {
rootPartition = KPMHelpers::createNewPartition( rootPartition = KPMHelpers::createNewPartition(
dev->partitionTable(), dev->partitionTable(),
@ -216,7 +190,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass
FileSystem::typeForName( defaultFsType ), FileSystem::typeForName( defaultFsType ),
firstFreeSector, firstFreeSector,
lastSectorForRoot, lastSectorForRoot,
luksPassphrase o.luksPassphrase
); );
} }
PartitionInfo::setFormat( rootPartition, true ); PartitionInfo::setFormat( rootPartition, true );
@ -226,7 +200,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass
if ( shouldCreateSwap ) if ( shouldCreateSwap )
{ {
Partition* swapPartition = nullptr; Partition* swapPartition = nullptr;
if ( luksPassphrase.isEmpty() ) if ( o.luksPassphrase.isEmpty() )
{ {
swapPartition = KPMHelpers::createNewPartition( swapPartition = KPMHelpers::createNewPartition(
dev->partitionTable(), dev->partitionTable(),
@ -246,7 +220,7 @@ doAutopartition( PartitionCoreModule* core, Device* dev, const QString& luksPass
FileSystem::LinuxSwap, FileSystem::LinuxSwap,
lastSectorForRoot + 1, lastSectorForRoot + 1,
dev->totalLogical() - 1, dev->totalLogical() - 1,
luksPassphrase o.luksPassphrase
); );
} }
PartitionInfo::setFormat( swapPartition, true ); PartitionInfo::setFormat( swapPartition, true );
@ -261,13 +235,11 @@ void
doReplacePartition( PartitionCoreModule* core, doReplacePartition( PartitionCoreModule* core,
Device* dev, Device* dev,
Partition* partition, Partition* partition,
const QString& luksPassphrase ) Choices::ReplacePartitionOptions o )
{ {
cDebug() << "doReplacePartition for device" << partition->partitionPath(); cDebug() << "doReplacePartition for device" << partition->partitionPath();
QString defaultFsType = Calamares::JobQueue::instance()-> QString defaultFsType = o.defaultFsType;
globalStorage()->
value( "defaultFileSystemType" ).toString();
if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown ) if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown )
defaultFsType = "ext4"; defaultFsType = "ext4";
@ -288,7 +260,7 @@ doReplacePartition( PartitionCoreModule* core,
} }
Partition* newPartition = nullptr; Partition* newPartition = nullptr;
if ( luksPassphrase.isEmpty() ) if ( o.luksPassphrase.isEmpty() )
{ {
newPartition = KPMHelpers::createNewPartition( newPartition = KPMHelpers::createNewPartition(
partition->parent(), partition->parent(),
@ -308,7 +280,7 @@ doReplacePartition( PartitionCoreModule* core,
FileSystem::typeForName( defaultFsType ), FileSystem::typeForName( defaultFsType ),
partition->firstSector(), partition->firstSector(),
partition->lastSector(), partition->lastSector(),
luksPassphrase o.luksPassphrase
); );
} }
PartitionInfo::setMountPoint( newPartition, "/" ); PartitionInfo::setMountPoint( newPartition, "/" );

View File

@ -27,29 +27,74 @@ class Partition;
namespace PartitionActions namespace PartitionActions
{ {
/** @brief Namespace for enums
*
* This namespace houses non-class enums.....
*/
namespace Choices
{
/** @brief Ccchoice of swap (size and type) */
enum SwapChoice
{
NoSwap, // don't create any swap, don't use any
ReuseSwap, // don't create, but do use existing
SmallSwap, // up to 8GiB of swap
FullSwap, // ensureSuspendToDisk -- at least RAM size
SwapFile // use a file (if supported)
};
struct ReplacePartitionOptions
{
QString defaultFsType; // e.g. "ext4" or "btrfs"
QString luksPassphrase; // optional
ReplacePartitionOptions( const QString& fs, const QString& luks )
: defaultFsType( fs )
, luksPassphrase( luks )
{
}
};
struct AutoPartitionOptions : ReplacePartitionOptions
{
QString efiPartitionMountPoint; // optional, e.g. "/boot"
quint64 requiredSpaceB; // estimated required space for root partition
SwapChoice swap;
AutoPartitionOptions( const QString& fs, const QString& luks, const QString& efi, qint64 r, SwapChoice s )
: ReplacePartitionOptions( fs, luks )
, efiPartitionMountPoint( efi )
, requiredSpaceB( r > 0 ? r : 0 )
, swap( s )
{
}
};
} // namespace Choices
/** /**
* @brief doAutopartition sets up an autopartitioning operation on the given Device. * @brief doAutopartition sets up an autopartitioning operation on the given Device.
* @param core a pointer to the PartitionCoreModule instance. * @param core a pointer to the PartitionCoreModule instance.
* @param dev the device to wipe. * @param dev the device to wipe.
* @param luksPassphrase the passphrase for LUKS encryption (optional, default is empty). * @param options settings for autopartitioning.
*/ */
void doAutopartition( PartitionCoreModule* core, void doAutopartition( PartitionCoreModule* core,
Device* dev, Device* dev,
const QString& luksPassphrase = QString() ); Choices::AutoPartitionOptions options );
/** /**
* @brief doReplacePartition sets up replace-partitioning with the given partition. * @brief doReplacePartition sets up replace-partitioning with the given partition.
* @param core a pointer to the PartitionCoreModule instance. * @param core a pointer to the PartitionCoreModule instance.
* @param dev a pointer to the Device on which to replace a partition. * @param dev a pointer to the Device on which to replace a partition.
* @param partition a pointer to the Partition to be replaced. * @param partition a pointer to the Partition to be replaced.
* @param luksPassphrase the passphrase for LUKS encryption (optional, default is empty). * @param options settings for partitioning (not all fields apply)
*
* @note this function also takes care of requesting PCM to delete the partition. * @note this function also takes care of requesting PCM to delete the partition.
*/ */
void doReplacePartition( PartitionCoreModule* core, void doReplacePartition( PartitionCoreModule* core,
Device* dev, Device* dev,
Partition* partition, Partition* partition,
const QString& luksPassphrase = QString() ); Choices::ReplacePartitionOptions options );
} } // namespace PartitionActions
#endif // PARTITIONACTIONS_H #endif // PARTITIONACTIONS_H

View File

@ -20,31 +20,33 @@
#include "ChoicePage.h" #include "ChoicePage.h"
#include "core/BootLoaderModel.h" #include "core/BootLoaderModel.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h"
#include "core/DeviceModel.h" #include "core/DeviceModel.h"
#include "core/PartitionModel.h" #include "core/KPMHelpers.h"
#include "core/OsproberEntry.h" #include "core/OsproberEntry.h"
#include "core/PartUtils.h" #include "core/PartUtils.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h"
#include "core/PartitionInfo.h"
#include "core/PartitionIterator.h" #include "core/PartitionIterator.h"
#include "core/PartitionModel.h"
#include "ReplaceWidget.h" #include "BootInfoWidget.h"
#include "PrettyRadioButton.h" #include "DeviceInfoWidget.h"
#include "PartitionBarsView.h" #include "PartitionBarsView.h"
#include "PartitionLabelsView.h" #include "PartitionLabelsView.h"
#include "PartitionSplitterWidget.h" #include "PartitionSplitterWidget.h"
#include "BootInfoWidget.h" #include "PrettyRadioButton.h"
#include "DeviceInfoWidget.h" #include "ReplaceWidget.h"
#include "ScanningDialog.h" #include "ScanningDialog.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Retranslator.h" #include "utils/Retranslator.h"
#include "utils/Units.h"
#include "Branding.h" #include "Branding.h"
#include "core/KPMHelpers.h"
#include "JobQueue.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "core/PartitionInfo.h" #include "JobQueue.h"
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
@ -61,7 +63,7 @@
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
using PartitionActions::Choices::SwapChoice;
/** /**
* @brief ChoicePage::ChoicePage is the default constructor. Called on startup as part of * @brief ChoicePage::ChoicePage is the default constructor. Called on startup as part of
@ -81,6 +83,9 @@ ChoicePage::ChoicePage( QWidget* parent )
, m_eraseButton( nullptr ) , m_eraseButton( nullptr )
, m_replaceButton( nullptr ) , m_replaceButton( nullptr )
, m_somethingElseButton( nullptr ) , m_somethingElseButton( nullptr )
, m_eraseSwapChoices( nullptr )
, m_replaceSwapChoices( nullptr )
, m_alongsideSwapChoices( nullptr )
, m_deviceInfoWidget( nullptr ) , m_deviceInfoWidget( nullptr )
, m_beforePartitionBarsView( nullptr ) , m_beforePartitionBarsView( nullptr )
, m_beforePartitionLabelsView( nullptr ) , m_beforePartitionLabelsView( nullptr )
@ -176,6 +181,19 @@ ChoicePage::init( PartitionCoreModule* core )
} }
/** @brief Creates a combobox with the given choices in it.
*
* No texts are set -- that happens later by the translator functions.
*/
static inline QComboBox*
createCombo( std::initializer_list< SwapChoice > l )
{
QComboBox* box = new QComboBox;
for ( SwapChoice c : l )
box->addItem( QString(), c );
return box;
}
/** /**
* @brief ChoicePage::setupChoices creates PrettyRadioButton objects for the action * @brief ChoicePage::setupChoices creates PrettyRadioButton objects for the action
* choices. * choices.
@ -229,6 +247,19 @@ ChoicePage::setupChoices()
iconSize ) ); iconSize ) );
m_grp->addButton( m_replaceButton->buttonWidget(), Replace ); m_grp->addButton( m_replaceButton->buttonWidget(), Replace );
// Fill up swap options
// .. TODO: only if enabled in the config
m_eraseSwapChoices = createCombo( { SwapChoice::NoSwap, SwapChoice::SmallSwap, SwapChoice:: FullSwap } );
m_eraseButton->addOptionsComboBox( m_eraseSwapChoices );
#if 0
m_replaceSwapChoices = createCombo( { SwapChoice::NoSwap, SwapChoice::ReuseSwap, SwapChoice::SmallSwap, SwapChoice::FullSwap } );
m_replaceButton->addOptionsComboBox( m_replaceSwapChoices );
m_alongsideSwapChoices = createCombo( { SwapChoice::NoSwap, SwapChoice::ReuseSwap, SwapChoice::SmallSwap, SwapChoice::FullSwap } );
m_alongsideButton->addOptionsComboBox( m_alongsideSwapChoices );
#endif
m_itemsLayout->addWidget( m_alongsideButton ); m_itemsLayout->addWidget( m_alongsideButton );
m_itemsLayout->addWidget( m_replaceButton ); m_itemsLayout->addWidget( m_replaceButton );
m_itemsLayout->addWidget( m_eraseButton ); m_itemsLayout->addWidget( m_eraseButton );
@ -252,7 +283,7 @@ ChoicePage::setupChoices()
{ {
if ( checked ) // An action was picked. if ( checked ) // An action was picked.
{ {
m_choice = static_cast< Choice >( id ); m_choice = static_cast< InstallChoice >( id );
updateNextEnabled(); updateNextEnabled();
emit actionChosen(); emit actionChosen();
@ -282,6 +313,12 @@ ChoicePage::setupChoices()
applyActionChoice( currentChoice() ); applyActionChoice( currentChoice() );
} }
} ); } );
CALAMARES_RETRANSLATE(
updateSwapChoicesTr( m_eraseSwapChoices );
updateSwapChoicesTr( m_alongsideSwapChoices );
updateSwapChoicesTr( m_replaceSwapChoices );
)
} }
@ -376,7 +413,7 @@ ChoicePage::continueApplyDeviceChoice()
void void
ChoicePage::applyActionChoice( ChoicePage::Choice choice ) ChoicePage::applyActionChoice( ChoicePage::InstallChoice choice )
{ {
m_beforePartitionBarsView->selectionModel()-> m_beforePartitionBarsView->selectionModel()->
disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) ); disconnect( SIGNAL( currentRowChanged( QModelIndex, QModelIndex ) ) );
@ -386,6 +423,17 @@ ChoicePage::applyActionChoice( ChoicePage::Choice choice )
switch ( choice ) switch ( choice )
{ {
case Erase: case Erase:
{
auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionActions::Choices::AutoPartitionOptions options {
gs->value( "defaultFileSystemType" ).toString(),
m_encryptWidget->passphrase(),
gs->value( "efiSystemPartition" ).toString(),
CalamaresUtils::GiBtoBytes( gs->value( "requiredStorageGB" ).toDouble() ),
static_cast<PartitionActions::Choices::SwapChoice>( m_eraseSwapChoices->currentData().toInt() )
};
if ( m_core->isDirty() ) if ( m_core->isDirty() )
{ {
ScanningDialog::run( QtConcurrent::run( [ = ] ScanningDialog::run( QtConcurrent::run( [ = ]
@ -395,21 +443,17 @@ ChoicePage::applyActionChoice( ChoicePage::Choice choice )
} ), } ),
[ = ] [ = ]
{ {
PartitionActions::doAutopartition( m_core, PartitionActions::doAutopartition( m_core, selectedDevice(), options );
selectedDevice(),
m_encryptWidget->passphrase() );
emit deviceChosen(); emit deviceChosen();
}, },
this ); this );
} }
else else
{ {
PartitionActions::doAutopartition( m_core, PartitionActions::doAutopartition( m_core, selectedDevice(), options );
selectedDevice(),
m_encryptWidget->passphrase() );
emit deviceChosen(); emit deviceChosen();
} }
}
break; break;
case Replace: case Replace:
if ( m_core->isDirty() ) if ( m_core->isDirty() )
@ -487,6 +531,7 @@ ChoicePage::doAlongsideSetupSplitter( const QModelIndex& current,
->value( "requiredStorageGB" ) ->value( "requiredStorageGB" )
.toDouble(); .toDouble();
// TODO: make this consistent
qint64 requiredStorageB = qRound64( requiredStorageGB + 0.1 + 2.0 ) * 1024 * 1024 * 1024; qint64 requiredStorageB = qRound64( requiredStorageGB + 0.1 + 2.0 ) * 1024 * 1024 * 1024;
m_afterPartitionSplitterWidget->setSplitPartition( m_afterPartitionSplitterWidget->setSplitPartition(
@ -771,14 +816,19 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current )
if ( homePartitionPath->isEmpty() ) if ( homePartitionPath->isEmpty() )
doReuseHomePartition = false; doReuseHomePartition = false;
PartitionActions::doReplacePartition( m_core, Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
PartitionActions::doReplacePartition(
m_core,
selectedDevice(), selectedDevice(),
selectedPartition, selectedPartition,
m_encryptWidget->passphrase() ); {
gs->value( "defaultFileSystemType" ).toString(),
m_encryptWidget->passphrase()
} );
Partition* homePartition = KPMHelpers::findPartitionByPath( { selectedDevice() }, Partition* homePartition = KPMHelpers::findPartitionByPath( { selectedDevice() },
*homePartitionPath ); *homePartitionPath );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( homePartition && doReuseHomePartition ) if ( homePartition && doReuseHomePartition )
{ {
PartitionInfo::setMountPoint( homePartition, "/home" ); PartitionInfo::setMountPoint( homePartition, "/home" );
@ -891,7 +941,7 @@ ChoicePage::updateDeviceStatePreview()
* @param choice the chosen partitioning action. * @param choice the chosen partitioning action.
*/ */
void void
ChoicePage::updateActionChoicePreview( ChoicePage::Choice choice ) ChoicePage::updateActionChoicePreview( ChoicePage::InstallChoice choice )
{ {
Device* currentDevice = selectedDevice(); Device* currentDevice = selectedDevice();
Q_ASSERT( currentDevice ); Q_ASSERT( currentDevice );
@ -1380,7 +1430,7 @@ ChoicePage::isNextEnabled() const
} }
ChoicePage::Choice ChoicePage::InstallChoice
ChoicePage::currentChoice() const ChoicePage::currentChoice() const
{ {
return m_choice; return m_choice;
@ -1428,3 +1478,42 @@ ChoicePage::updateNextEnabled()
emit nextStatusChanged( enabled ); emit nextStatusChanged( enabled );
} }
void
ChoicePage::updateSwapChoicesTr(QComboBox* box)
{
if ( !box )
return;
static_assert(SwapChoice::NoSwap == 0, "Enum values out-of-sync");
for ( int index = 0; index < box->count(); ++index )
{
bool ok = false;
int value = 0;
switch ( value = box->itemData( index ).toInt( &ok ) )
{
// case 0:
case SwapChoice::NoSwap:
// toInt() returns 0 on failure, so check for ok
if ( ok ) // It was explicitly set to 0
box->setItemText( index, tr( "No Swap" ) );
else
cWarning() << "Box item" << index << box->itemText( index ) << "has non-integer role.";
break;
case SwapChoice::ReuseSwap:
box->setItemText( index, tr( "Reuse Swap" ) );
break;
case SwapChoice::SmallSwap:
box->setItemText( index, tr( "Swap (no Hibernate)" ) );
break;
case SwapChoice::FullSwap:
box->setItemText( index, tr( "Swap (with Hibernate)" ) );
break;
case SwapChoice::SwapFile:
box->setItemText( index, tr( "Swap to file" ) );
break;
default:
cWarning() << "Box item" << index << box->itemText( index ) << "has role" << value;
}
}
}

View File

@ -53,7 +53,7 @@ class ChoicePage : public QWidget, private Ui::ChoicePage
{ {
Q_OBJECT Q_OBJECT
public: public:
enum Choice enum InstallChoice
{ {
NoChoice, NoChoice,
Alongside, Alongside,
@ -84,7 +84,7 @@ public:
* currently selected partitioning mode (with a PrettyRadioButton). * currently selected partitioning mode (with a PrettyRadioButton).
* @return the enum Choice value. * @return the enum Choice value.
*/ */
Choice currentChoice() const; InstallChoice currentChoice() const;
/** /**
* @brief onLeave runs when control passes from this page to another one. * @brief onLeave runs when control passes from this page to another one.
@ -95,7 +95,7 @@ public:
* @brief applyActionChoice reacts to a choice of partitioning mode. * @brief applyActionChoice reacts to a choice of partitioning mode.
* @param choice the partitioning action choice. * @param choice the partitioning action choice.
*/ */
void applyActionChoice( ChoicePage::Choice choice ); void applyActionChoice( ChoicePage::InstallChoice choice );
signals: signals:
void nextStatusChanged( bool ); void nextStatusChanged( bool );
@ -121,18 +121,21 @@ private:
void continueApplyDeviceChoice(); // .. called after scan void continueApplyDeviceChoice(); // .. called after scan
void updateDeviceStatePreview(); void updateDeviceStatePreview();
void updateActionChoicePreview( ChoicePage::Choice choice ); void updateActionChoicePreview( ChoicePage::InstallChoice choice );
void setupActions(); void setupActions();
OsproberEntryList getOsproberEntriesForDevice( Device* device ) const; OsproberEntryList getOsproberEntriesForDevice( Device* device ) const;
void doAlongsideApply(); void doAlongsideApply();
void setupEfiSystemPartitionSelector(); void setupEfiSystemPartitionSelector();
// Translations support
void updateSwapChoicesTr( QComboBox* box );
bool m_nextEnabled; bool m_nextEnabled;
PartitionCoreModule* m_core; PartitionCoreModule* m_core;
QMutex m_previewsMutex; QMutex m_previewsMutex;
Choice m_choice; InstallChoice m_choice;
bool m_isEfi; bool m_isEfi;
QComboBox* m_drivesCombo; QComboBox* m_drivesCombo;
@ -142,6 +145,9 @@ private:
PrettyRadioButton* m_eraseButton; PrettyRadioButton* m_eraseButton;
PrettyRadioButton* m_replaceButton; PrettyRadioButton* m_replaceButton;
PrettyRadioButton* m_somethingElseButton; PrettyRadioButton* m_somethingElseButton;
QComboBox* m_eraseSwapChoices;
QComboBox* m_replaceSwapChoices;
QComboBox* m_alongsideSwapChoices;
DeviceInfoWidget* m_deviceInfoWidget; DeviceInfoWidget* m_deviceInfoWidget;

View File

@ -21,6 +21,7 @@
#include "gui/PartitionViewStep.h" #include "gui/PartitionViewStep.h"
#include "core/DeviceModel.h" #include "core/DeviceModel.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h" #include "core/PartitionCoreModule.h"
#include "core/PartitionModel.h" #include "core/PartitionModel.h"
#include "core/KPMHelpers.h" #include "core/KPMHelpers.h"
@ -138,7 +139,7 @@ PartitionViewStep::createSummaryWidget() const
widget->setLayout( mainLayout ); widget->setLayout( mainLayout );
mainLayout->setMargin( 0 ); mainLayout->setMargin( 0 );
ChoicePage::Choice choice = m_choicePage->currentChoice(); ChoicePage::InstallChoice choice = m_choicePage->currentChoice();
QFormLayout* formLayout = new QFormLayout( widget ); QFormLayout* formLayout = new QFormLayout( widget );
const int MARGIN = CalamaresUtils::defaultFontHeight() / 2; const int MARGIN = CalamaresUtils::defaultFontHeight() / 2;
@ -470,92 +471,115 @@ PartitionViewStep::onLeave()
} }
static PartitionActions::Choices::SwapChoice
nameToChoice( QString name, bool& ok )
{
ok = false;
name = name.toLower();
using namespace PartitionActions::Choices;
// Each return here first sets ok to true, returns enum value
if ( name == QStringLiteral( "none" ) )
return( ok=true, SwapChoice::NoSwap );
else if ( name == QStringLiteral( "small" ) )
return( ok=true, SwapChoice::SmallSwap);
else if ( name == QStringLiteral( "suspend" ) )
return( ok=true, SwapChoice::FullSwap );
else if ( name == QStringLiteral( "reuse" ) )
return( ok=true, SwapChoice::ReuseSwap );
else if ( name == QStringLiteral( "file" ) )
return( ok=true, SwapChoice::SwapFile );
ok = false;
return SwapChoice::NoSwap;
}
void void
PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap ) PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
// Copy the efiSystemPartition setting to the global storage. It is needed not only in // Copy the efiSystemPartition setting to the global storage. It is needed not only in
// the EraseDiskPage, but also in the bootloader configuration modules (grub, bootloader). // the EraseDiskPage, but also in the bootloader configuration modules (grub, bootloader).
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( configurationMap.contains( "efiSystemPartition" ) && QString efiSP = CalamaresUtils::getString( configurationMap, "efiSystemPartition" );
configurationMap.value( "efiSystemPartition" ).type() == QVariant::String && if ( efiSP.isEmpty() )
!configurationMap.value( "efiSystemPartition" ).toString().isEmpty() ) efiSP = QStringLiteral( "/boot/efi" );
gs->insert( "efiSystemPartition", efiSP );
// SWAP SETTINGS
//
// This is a bit convoluted because there's legacy settings to handle as well
// as the new-style list of choices, with mapping back-and-forth.
if ( configurationMap.contains( "userSwapChoices" ) &&
( configurationMap.contains( "ensureSuspendToDisk" ) || configurationMap.contains( "neverCreateSwap" ) ) )
cError() << "Partition-module configuration mixes old- and new-style swap settings.";
if ( configurationMap.contains( "ensureSuspendToDisk" ) )
cWarning() << "Partition-module setting *ensureSuspendToDisk* is deprecated.";
bool ensureSuspendToDisk = CalamaresUtils::getBool( configurationMap, "ensureSuspendToDisk", true );
if ( configurationMap.contains( "neverCreateSwap" ) )
cWarning() << "Partition-module setting *neverCreateSwap* is deprecated.";
bool neverCreateSwap = CalamaresUtils::getBool( configurationMap, "neverCreateSwap", false );
QSet< PartitionActions::Choices::SwapChoice > choices; // Available swap choices
if ( configurationMap.contains( "userSwapChoices" ) )
{ {
gs->insert( "efiSystemPartition", configurationMap.value( "efiSystemPartition" ).toString() ); // We've already warned about overlapping settings with the
} // legacy *ensureSuspendToDisk* and *neverCreateSwap*.
else QStringList l = configurationMap[ "userSwapChoices" ].toStringList();
for ( const auto& item : l )
{ {
gs->insert( "efiSystemPartition", QStringLiteral( "/boot/efi" ) ); bool ok = false;
auto v = nameToChoice( item, ok );
if ( ok )
choices.insert( v );
} }
if ( configurationMap.contains( "ensureSuspendToDisk" ) && if ( choices.isEmpty() )
configurationMap.value( "ensureSuspendToDisk" ).type() == QVariant::Bool )
{ {
gs->insert( "ensureSuspendToDisk", configurationMap.value( "ensureSuspendToDisk" ).toBool() ); cWarning() << "Partition-module configuration for *userSwapChoices* is empty:" << l;
} choices.insert( PartitionActions::Choices::SwapChoice::FullSwap );
else
{
gs->insert( "ensureSuspendToDisk", true );
} }
if ( configurationMap.contains( "neverCreateSwap" ) && // suspend if it's one of the possible choices; suppress swap only if it's
configurationMap.value( "neverCreateSwap" ).type() == QVariant::Bool ) // the **only** choice available.
{ ensureSuspendToDisk = choices.contains( PartitionActions::Choices::SwapChoice::FullSwap );
gs->insert( "neverCreateSwap", configurationMap.value( "neverCreateSwap" ).toBool() ); neverCreateSwap = ( choices.count() == 1 ) && choices.contains( PartitionActions::Choices::SwapChoice::NoSwap );
} }
else else
{ {
gs->insert( "neverCreateSwap", false ); // Convert the legacy settings into a single setting for now.
if ( neverCreateSwap )
choices.insert( PartitionActions::Choices::SwapChoice::NoSwap );
else if ( ensureSuspendToDisk )
choices.insert( PartitionActions::Choices::SwapChoice::FullSwap );
else
choices.insert( PartitionActions::Choices::SwapChoice::SmallSwap );
} }
if ( configurationMap.contains( "drawNestedPartitions" ) && // These gs settings seem to be unused (in upstream Calamares) outside of
configurationMap.value( "drawNestedPartitions" ).type() == QVariant::Bool ) // the partition module itself.
{ gs->insert( "ensureSuspendToDisk", ensureSuspendToDisk );
gs->insert( "drawNestedPartitions", gs->insert( "neverCreateSwap", neverCreateSwap );
configurationMap.value( "drawNestedPartitions", false ).toBool() );
}
else
{
gs->insert( "drawNestedPartitions", false );
}
if ( configurationMap.contains( "alwaysShowPartitionLabels" ) && // OTHER SETTINGS
configurationMap.value( "alwaysShowPartitionLabels" ).type() == QVariant::Bool ) //
{ gs->insert( "drawNestedPartitions", CalamaresUtils::getBool( configurationMap, "drawNestedPartitions", false ) );
gs->insert( "alwaysShowPartitionLabels", gs->insert( "alwaysShowPartitionLabels", CalamaresUtils::getBool( configurationMap, "alwaysShowPartitionLabels", true ) );
configurationMap.value( "alwaysShowPartitionLabels", true ).toBool() ); gs->insert( "enableLuksAutomatedPartitioning", CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) );
}
else
{
gs->insert( "alwaysShowPartitionLabels", true );
}
if ( configurationMap.contains( "defaultFileSystemType" ) && QString defaultFS = CalamaresUtils::getString( configurationMap, "defaultFileSystemType" );
configurationMap.value( "defaultFileSystemType" ).type() == QVariant::String && if ( defaultFS.isEmpty() )
!configurationMap.value( "defaultFileSystemType" ).toString().isEmpty() ) defaultFS = QStringLiteral( "ext4" );
if ( FileSystem::typeForName( defaultFS ) == FileSystem::Unknown )
{ {
QString typeString = configurationMap.value( "defaultFileSystemType" ).toString(); cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << defaultFS << ") using ext4.";
gs->insert( "defaultFileSystemType", typeString ); defaultFS = QStringLiteral( "ext4" );
if ( FileSystem::typeForName( typeString ) == FileSystem::Unknown )
{
cWarning() << "bad default filesystem configuration for partition module. Reverting to ext4 as default.";
gs->insert( "defaultFileSystemType", "ext4" );
}
}
else
{
gs->insert( "defaultFileSystemType", QStringLiteral( "ext4" ) );
}
if ( configurationMap.contains( "enableLuksAutomatedPartitioning" ) &&
configurationMap.value( "enableLuksAutomatedPartitioning" ).type() == QVariant::Bool )
{
gs->insert( "enableLuksAutomatedPartitioning",
configurationMap.value( "enableLuksAutomatedPartitioning" ).toBool() );
}
else
{
gs->insert( "enableLuksAutomatedPartitioning", true );
} }
gs->insert( "defaultFileSystemType", defaultFS );
// Now that we have the config, we load the PartitionCoreModule in the background // Now that we have the config, we load the PartitionCoreModule in the background
@ -563,7 +587,7 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
// and remove the spinner. // and remove the spinner.
QFutureWatcher< void >* watcher = new QFutureWatcher< void >(); QFutureWatcher< void >* watcher = new QFutureWatcher< void >();
connect( watcher, &QFutureWatcher< void >::finished, connect( watcher, &QFutureWatcher< void >::finished,
this, [ this, watcher ] this, [ this, watcher, choices ]
{ {
continueLoading(); continueLoading();
watcher->deleteLater(); watcher->deleteLater();

View File

@ -21,29 +21,34 @@
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "widgets/ClickableLabel.h" #include "widgets/ClickableLabel.h"
#include <QComboBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel> #include <QLabel>
#include <QBoxLayout>
PrettyRadioButton::PrettyRadioButton( QWidget* parent ) PrettyRadioButton::PrettyRadioButton( QWidget* parent )
: QWidget( parent ) : QWidget( parent )
, m_label( new ClickableLabel )
, m_radio( new QRadioButton )
, m_mainLayout( new QGridLayout )
, m_optionsLayout( nullptr )
{ {
QHBoxLayout* mainLayout = new QHBoxLayout; setLayout( m_mainLayout );
setLayout( mainLayout );
m_radio = new QRadioButton;
m_label = new ClickableLabel;
connect( m_label, &ClickableLabel::clicked,
m_radio, &QRadioButton::click );
m_label->setBuddy( m_radio ); m_label->setBuddy( m_radio );
m_label->setWordWrap( true ); m_label->setWordWrap( true );
m_label->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); m_label->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
mainLayout->addWidget( m_radio ); m_mainLayout->addWidget( m_radio, 0, 0 );
mainLayout->addWidget( m_label ); m_mainLayout->addWidget( m_label, 0, 1 );
mainLayout->setContentsMargins( 0, 0, 0, 0 ); m_mainLayout->setContentsMargins( 0, 0, 0, 0 );
connect( m_label, &ClickableLabel::clicked,
m_radio, &QRadioButton::click );
connect( m_radio, &QRadioButton::toggled,
this, &PrettyRadioButton::toggleOptions );
} }
@ -80,3 +85,32 @@ PrettyRadioButton::buttonWidget() const
{ {
return m_radio; return m_radio;
} }
void
PrettyRadioButton::addOptionsComboBox( QComboBox* box )
{
if ( !box )
return;
if ( !m_optionsLayout )
{
QWidget* w = new QWidget;
m_optionsLayout = new QHBoxLayout;
m_optionsLayout->setAlignment( Qt::AlignmentFlag::AlignLeft );
m_optionsLayout->addStretch( 1 );
w->setLayout( m_optionsLayout );
m_mainLayout->addWidget( w, 1, 1 );
toggleOptions( m_radio->isChecked() );
}
m_optionsLayout->insertWidget( m_optionsLayout->count()-1, box );
}
void
PrettyRadioButton::toggleOptions( bool toggle )
{
if ( m_optionsLayout )
m_optionsLayout->parentWidget()->setVisible( toggle );
}

View File

@ -22,7 +22,17 @@
#include <QRadioButton> #include <QRadioButton>
class ClickableLabel; class ClickableLabel;
class QComboBox;
class QGridLayout;
class QHBoxLayout;
/** @brief A radio button with fancy label next to it.
*
* The radio button itself can be retrieved with buttonWidget(),
* and the whole behaves a lot like a label. Extra options can be
* added to the display (options are hidden when the button is
* not selected) with addOptionsComboBox().
*/
class PrettyRadioButton : public QWidget class PrettyRadioButton : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -40,9 +50,18 @@ public:
virtual QRadioButton* buttonWidget() const; virtual QRadioButton* buttonWidget() const;
/** @brief Add an options drop-down to this button. */
void addOptionsComboBox( QComboBox* );
protected slots:
/// Options are hidden when the radio button is off
void toggleOptions( bool checked );
protected: protected:
ClickableLabel* m_label; ClickableLabel* m_label;
QRadioButton* m_radio; QRadioButton* m_radio;
QGridLayout* m_mainLayout;
QHBoxLayout* m_optionsLayout;
}; };
#endif // PRETTYRADIOBUTTON_H #endif // PRETTYRADIOBUTTON_H

View File

@ -85,6 +85,8 @@ ReplaceWidget::reset()
void void
ReplaceWidget::applyChanges() ReplaceWidget::applyChanges()
{ {
auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionModel* model = qobject_cast< PartitionModel* >( m_ui->partitionTreeView->model() ); PartitionModel* model = qobject_cast< PartitionModel* >( m_ui->partitionTreeView->model() );
if ( model ) if ( model )
{ {
@ -93,7 +95,9 @@ ReplaceWidget::applyChanges()
{ {
Device* dev = model->device(); Device* dev = model->device();
PartitionActions::doReplacePartition( m_core, dev, partition ); PartitionActions::doReplacePartition(
m_core, dev, partition,
{ gs->value( "defaultFileSystemType" ).toString(), QString() } );
if ( m_isEfi ) if ( m_isEfi )
{ {
@ -102,17 +106,13 @@ ReplaceWidget::applyChanges()
{ {
PartitionInfo::setMountPoint( PartitionInfo::setMountPoint(
efiSystemPartitions.first(), efiSystemPartitions.first(),
Calamares::JobQueue::instance()-> gs->value( "efiSystemPartition" ).toString() );
globalStorage()->
value( "efiSystemPartition" ).toString() );
} }
else if ( efiSystemPartitions.count() > 1 ) else if ( efiSystemPartitions.count() > 1 )
{ {
PartitionInfo::setMountPoint( PartitionInfo::setMountPoint(
efiSystemPartitions.at( m_ui->bootComboBox->currentIndex() ), efiSystemPartitions.at( m_ui->bootComboBox->currentIndex() ),
Calamares::JobQueue::instance()-> gs->value( "efiSystemPartition" ).toString() );
globalStorage()->
value( "efiSystemPartition" ).toString() );
} }
} }

View File

@ -3,22 +3,37 @@
# etc.) use just /boot. # etc.) use just /boot.
efiSystemPartition: "/boot/efi" efiSystemPartition: "/boot/efi"
# Make sure an autogenerated swap partition is big enough for hibernation in # In autogenerated partitioning, allow the user to select a swap size?
# automated partitioning modes. Swap can be disabled through *neverCreateSwap*. # If there is exactly one choice, no UI is presented, and the user
# cannot make a choice -- this setting is used. If there is more than
# one choice, a UI is presented.
# #
# When *ensureSuspendToDisk* is true, swap is never smaller than physical # Legacy settings *neverCreateSwap* and *ensureSuspendToDisk* correspond
# memory, follows the guideline 2 * memory until swap reaches 8GiB. # to values of *userSwapChoices* as follows:
# When *ensureSuspendToDisk* is false, swap size scales up with memory # - *neverCreateSwap* is true, means [none]
# size until 8GiB, then at roughly half of memory size. # - *neverCreateSwap* is false, *ensureSuspendToDisk* is false, [small]
# - *neverCreateSwap* is false, *ensureSuspendToDisk* is true, [suspend]
# #
# # Autogenerated swap sizes are as follows:
# Default is true. # - *suspend*: Swap is always at least total memory size,
ensureSuspendToDisk: true # and up to 4GiB RAM follows the rule-of-thumb 2 * memory;
# from 4GiB to 8 GiB it stays steady at 8GiB, and over 8 GiB memory
# swap is the size of main memory.
# - *small*: Follows the rules above, but Swap is at
# most 8GiB, and no more than 10% of available disk.
# In both cases, a fudge factor (usually 10% extra) is applied so that there
# is some space for administrative overhead (e.g. 8 GiB swap will allocate
# 8.8GiB on disk in the end).
userSwapChoices:
- none # Create no swap, use no swap
# - reuse # Re-use existing swap, but don't create any
- small # Up to 4GB
- suspend # At least main memory size
# - file # To swap file instead of partition (unsupported right now)
# Never create swap partitions in automated partitioning modes. # LEGACY SETTINGS (these will generate a warning)
# If this is true, ensureSuspendToDisk is ignored. # ensureSuspendToDisk: true
# Default is false. # neverCreateSwap: false
neverCreateSwap: false
# Correctly draw nested (e.g. logical) partitions as such. # Correctly draw nested (e.g. logical) partitions as such.
drawNestedPartitions: false drawNestedPartitions: false