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

This commit is contained in:
Philip Müller 2021-07-05 13:31:37 +02:00
commit 16c6e2c6d3
49 changed files with 832 additions and 379 deletions

13
CHANGES
View File

@ -10,12 +10,15 @@ website will have to do for older versions.
# 3.2.40 (unreleased) # # 3.2.40 (unreleased) #
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Anubhav Choudhary (SoK success!) - Anubhav Choudhary (SoK success!)
- Emmanuel Arias (new contributor! welcome!) - Emmanuel Arias (new contributor! welcome!)
- Erik Dubois - Erik Dubois
- Jerrod Frost (new contributor! welcome!) - Jerrod Frost (new contributor! welcome!)
- Jia Chao (new contributor! welcome!)
- Joe Kamprad - Joe Kamprad
- Lisa Vitolo (blast from the past!) - Lisa Vitolo (blast from the past!)
- Omer I.S. (new contributor! welcome!)
In project news, chat (instant-messaging) communications has largely In project news, chat (instant-messaging) communications has largely
moved to Matrix and Libera.Chat. CI notifications -- issues and build moved to Matrix and Libera.Chat. CI notifications -- issues and build
@ -33,9 +36,14 @@ results -- are sent to Matrix only.
- The "upload log file" now has a configurable log-file-size. (Thanks Anubhav) - The "upload log file" now has a configurable log-file-size. (Thanks Anubhav)
## Modules ## ## Modules ##
- *bootloader* can now install an aarch64 (ARM) compatible EFI GRUB. (Thanks Jia)
- *displaymanager* example configuration has been shuffled around a bit, - *displaymanager* example configuration has been shuffled around a bit,
for better results when the live image is running XFCE. Also lists for better results when the live image is running XFCE. Also lists
more potential display managers. #1205 (Thanks Erik) more potential display managers. #1205 (Thanks Erik)
- *keyboard* now switches on an alternate `en_US` keyboard layout when
Arabic or Hebrew is selected as primary layout. (Thanks Omer)
- *localeq* now has a fully functional offline option (alongside the default
interactive map option, which requires internet).
- The *netinstall* module can now fall back to alternative URLs when - The *netinstall* module can now fall back to alternative URLs when
loading groups data. The first URL to yield a non-empty groups loading groups data. The first URL to yield a non-empty groups
collection is accepted. No changes are needed in the configuration. #1673 collection is accepted. No changes are needed in the configuration. #1673
@ -47,6 +55,11 @@ results -- are sent to Matrix only.
- A long-neglected pull request from Lisa Vitolo for the *partition* - A long-neglected pull request from Lisa Vitolo for the *partition*
module -- allowing to set filesystem labels during manual partitioning -- module -- allowing to set filesystem labels during manual partitioning --
has been revived and merged. has been revived and merged.
- The *partition* manager has had a long-standing bug with partition-flags
and manual partitioning resolved. This may help resolve some installation
issues on UEFI systems. #1724
- *usersq* is further implemented and can now be used for a successful install.
Not all warning messages available in the regular users module are implemented.
# 3.2.39.3 (2021-04-14) # # 3.2.39.3 (2021-04-14) #

View File

@ -18,18 +18,25 @@
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "Job.h" #include "Job.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "PythonJob.h"
#include "Settings.h" #include "Settings.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "modulesystem/Module.h" #include "modulesystem/Module.h"
#include "modulesystem/ModuleManager.h" #include "modulesystem/ModuleManager.h"
#include "modulesystem/ViewModule.h" #include "modulesystem/ViewModule.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/Yaml.h"
#include "viewpages/ExecutionViewStep.h"
// Optional features of Calamares
// - Python support
// - QML support
#ifdef WITH_PYTHON
#include "PythonJob.h"
#endif
#ifdef WITH_QML #ifdef WITH_QML
#include "utils/Qml.h" #include "utils/Qml.h"
#endif #endif
#include "utils/Yaml.h"
#include "viewpages/ExecutionViewStep.h"
#include <QApplication> #include <QApplication>
#include <QCommandLineOption> #include <QCommandLineOption>
@ -55,6 +62,7 @@ struct ModuleConfig
QString m_language; QString m_language;
QString m_branding; QString m_branding;
bool m_ui; bool m_ui;
bool m_pythonInjection;
}; };
static ModuleConfig static ModuleConfig
@ -79,7 +87,6 @@ handle_args( QCoreApplication& a )
QStringLiteral( "Enable UI" ) ); QStringLiteral( "Enable UI" ) );
QCommandLineOption slideshowOption( QStringList() << QStringLiteral( "s" ) << QStringLiteral( "slideshow" ), QCommandLineOption slideshowOption( QStringList() << QStringLiteral( "s" ) << QStringLiteral( "slideshow" ),
QStringLiteral( "Run slideshow module" ) ); QStringLiteral( "Run slideshow module" ) );
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription( "Calamares module tester" ); parser.setApplicationDescription( "Calamares module tester" );
parser.addHelpOption(); parser.addHelpOption();
@ -92,6 +99,12 @@ handle_args( QCoreApplication& a )
parser.addOption( brandOption ); parser.addOption( brandOption );
parser.addOption( uiOption ); parser.addOption( uiOption );
parser.addOption( slideshowOption ); parser.addOption( slideshowOption );
#ifdef WITH_PYTHON
QCommandLineOption pythonOption( QStringList() << QStringLiteral( "P" ) << QStringLiteral( "no-injected-python" ),
QStringLiteral( "Do not disable potentially-harmful Python commands" ) );
parser.addOption( pythonOption );
#endif
parser.addPositionalArgument( "module", "Path or name of module to run." ); parser.addPositionalArgument( "module", "Path or name of module to run." );
parser.addPositionalArgument( "job.yaml", "Path of job settings document to use.", "[job.yaml]" ); parser.addPositionalArgument( "job.yaml", "Path of job settings document to use.", "[job.yaml]" );
@ -116,12 +129,21 @@ handle_args( QCoreApplication& a )
jobSettings = args.at( 1 ); jobSettings = args.at( 1 );
} }
bool pythonInjection = true;
#ifdef WITH_PYTHON
if ( parser.isSet( pythonOption ) )
{
pythonInjection = false;
}
#endif
return ModuleConfig { parser.isSet( slideshowOption ) ? QStringLiteral( "-" ) : args.first(), return ModuleConfig { parser.isSet( slideshowOption ) ? QStringLiteral( "-" ) : args.first(),
jobSettings, jobSettings,
parser.value( globalOption ), parser.value( globalOption ),
parser.value( langOption ), parser.value( langOption ),
parser.value( brandOption ), parser.value( brandOption ),
parser.isSet( slideshowOption ) || parser.isSet( uiOption ) }; parser.isSet( slideshowOption ) || parser.isSet( uiOption ),
pythonInjection
};
} }
} }
@ -366,10 +388,15 @@ createApplication( int& argc, char* argv[] )
return new QCoreApplication( argc, argv ); return new QCoreApplication( argc, argv );
} }
#ifdef WITH_PYTHON
static const char pythonPreScript[] = R"( static const char pythonPreScript[] = R"(
# This is Python code executed by Python modules *before* the # This is Python code executed by Python modules *before* the
# script file (e.g. main.py) is executed. Beware " before ) # script file (e.g. main.py) is executed. Beware " before )
# because it's a C++ raw-string. # because it's a C++ raw-string.
#
# Calls to suprocess methods that execute something are
# suppressed and logged -- scripts should really be using libcalamares
# methods instead.
_calamares_subprocess = __import__("subprocess", globals(), locals(), [], 0) _calamares_subprocess = __import__("subprocess", globals(), locals(), [], 0)
import sys import sys
import libcalamares import libcalamares
@ -382,10 +409,13 @@ class fake_subprocess(object):
def check_call(*args, **kwargs): def check_call(*args, **kwargs):
libcalamares.utils.debug("subprocess.check_call(%r,%r) X subverted to call" % (args, kwargs)) libcalamares.utils.debug("subprocess.check_call(%r,%r) X subverted to call" % (args, kwargs))
return 0 return 0
for attr in ("CalledProcessError",):
setattr(fake_subprocess,attr,getattr(_calamares_subprocess,attr))
sys.modules["subprocess"] = fake_subprocess sys.modules["subprocess"] = fake_subprocess
libcalamares.utils.debug('pre-script for testing purposes injected') libcalamares.utils.debug('pre-script for testing purposes injected')
)"; )";
#endif
int int
main( int argc, char* argv[] ) main( int argc, char* argv[] )
@ -416,10 +446,15 @@ main( int argc, char* argv[] )
gs->insert( "localeConf", vm ); gs->insert( "localeConf", vm );
} }
#ifdef WITH_PYTHON
if ( module.m_pythonInjection )
{
Calamares::PythonJob::setInjectedPreScript(pythonPreScript);
}
#endif
#ifdef WITH_QML #ifdef WITH_QML
CalamaresUtils::initQmlModulesDir(); // don't care if failed CalamaresUtils::initQmlModulesDir(); // don't care if failed
#endif #endif
Calamares::PythonJob::setInjectedPreScript(pythonPreScript);
cDebug() << "Calamares module-loader testing" << module.moduleName(); cDebug() << "Calamares module-loader testing" << module.moduleName();
Calamares::Module* m = load_module( module ); Calamares::Module* m = load_module( module );

View File

@ -60,6 +60,8 @@ public:
friend CDebug& operator<<( CDebug&&, const FuncSuppressor& ); friend CDebug& operator<<( CDebug&&, const FuncSuppressor& );
friend CDebug& operator<<( CDebug&&, const Once& ); friend CDebug& operator<<( CDebug&&, const Once& );
inline unsigned int level() const { return m_debugLevel; }
private: private:
QString m_msg; QString m_msg;
unsigned int m_debugLevel; unsigned int m_debugLevel;
@ -315,6 +317,12 @@ private:
inline CDebug& inline CDebug&
operator<<( CDebug&& s, const Once& o ) operator<<( CDebug&& s, const Once& o )
{ {
if ( !logLevelEnabled( s.level() ) )
{
// This won't print, so it's not using the "onceness"
return s;
}
if ( o.m ) if ( o.m )
{ {
o.m = false; o.m = false;
@ -327,6 +335,7 @@ operator<<( CDebug&& s, const Once& o )
} // namespace Logger } // namespace Logger
#define cVerbose() Logger::CDebug( Logger::LOGVERBOSE, Q_FUNC_INFO )
#define cDebug() Logger::CDebug( Logger::LOGDEBUG, Q_FUNC_INFO ) #define cDebug() Logger::CDebug( Logger::LOGDEBUG, Q_FUNC_INFO )
#define cWarning() Logger::CDebug( Logger::LOGWARNING, Q_FUNC_INFO ) #define cWarning() Logger::CDebug( Logger::LOGWARNING, Q_FUNC_INFO )
#define cError() Logger::CDebug( Logger::LOGERROR, Q_FUNC_INFO ) #define cError() Logger::CDebug( Logger::LOGERROR, Q_FUNC_INFO )

View File

@ -14,6 +14,7 @@
#include <QObject> #include <QObject>
#include <QSignalBlocker> #include <QSignalBlocker>
#include <optional>
#include <type_traits> #include <type_traits>
/** @brief Convenience to zero out and deleteLater of any QObject-derived-class /** @brief Convenience to zero out and deleteLater of any QObject-derived-class
@ -58,4 +59,20 @@ struct cBoolSetter
/// @brief Blocks signals on a QObject until destruction /// @brief Blocks signals on a QObject until destruction
using cSignalBlocker = QSignalBlocker; using cSignalBlocker = QSignalBlocker;
/** @brief Writes a value on destruction to a pointed-to location.
*
* If the pointer is non-null, write the last-given-value if there
* is one to the pointed-to object.
*/
template < typename T >
struct cPointerSetter
{
std::optional< T > m_value;
T* m_pointer;
cPointerSetter( T* p ) : m_pointer(p) {}
~cPointerSetter() { if ( m_pointer && m_value.has_value() ) { *m_pointer = m_value.value(); } }
const T& operator=(const T& v) { m_value = v; return v; }
};
#endif #endif

View File

@ -46,25 +46,27 @@ private Q_SLOTS:
void testCommands(); void testCommands();
/** @brief Test that all the UMask objects work correctly. */ /** @section Test that all the UMask objects work correctly. */
void testUmask(); void testUmask();
/** @brief Tests the entropy functions. */ /** @section Tests the entropy functions. */
void testEntropy(); void testEntropy();
void testPrintableEntropy(); void testPrintableEntropy();
void testOddSizedPrintable(); void testOddSizedPrintable();
/** @brief Tests the RAII bits. */ /** @section Tests the RAII bits. */
void testBoolSetter(); void testBoolSetter();
void testPointerSetter();
/** @brief Tests the Traits bits. */ /** @section Tests the Traits bits. */
void testTraits(); void testTraits();
/** @section Testing the variants-methods */
void testVariantStringListCode(); void testVariantStringListCode();
void testVariantStringListYAMLDashed(); void testVariantStringListYAMLDashed();
void testVariantStringListYAMLBracketed(); void testVariantStringListYAMLBracketed();
/** @brief Test smart string truncation. */ /** @section Test smart string truncation. */
void testStringTruncation(); void testStringTruncation();
void testStringTruncationShorter(); void testStringTruncationShorter();
void testStringTruncationDegenerate(); void testStringTruncationDegenerate();
@ -360,6 +362,50 @@ LibCalamaresTests::testBoolSetter()
QVERIFY( b ); QVERIFY( b );
} }
void
LibCalamaresTests::testPointerSetter()
{
int special = 17;
QCOMPARE( special, 17 );
{
cPointerSetter p( &special );
}
QCOMPARE( special, 17 );
{
cPointerSetter p( &special );
p = 18;
}
QCOMPARE( special, 18 );
{
cPointerSetter p( &special );
p = 20;
p = 3;
}
QCOMPARE( special, 3 );
{
cPointerSetter<int> p( nullptr );
}
QCOMPARE( special, 3 );
{
// "don't do this" .. order of destructors is important
cPointerSetter p( &special );
cPointerSetter q( &special );
p = 17;
}
QCOMPARE( special, 17 );
{
// "don't do this" .. order of destructors is important
cPointerSetter p( &special );
cPointerSetter q( &special );
p = 34;
q = 2;
// q destroyed first, then p
}
QCOMPARE( special, 34 );
}
/* Demonstration of Traits support for has-a-method or not. /* Demonstration of Traits support for has-a-method or not.
* *
* We have two classes, c1 and c2; one has a method do_the_thing() and the * We have two classes, c1 and c2; one has a method do_the_thing() and the
@ -431,17 +477,31 @@ LibCalamaresTests::testVariantStringListCode()
QCOMPARE( getStringList( m, key ), QStringList {} ); QCOMPARE( getStringList( m, key ), QStringList {} );
m.insert( key, 17 ); m.insert( key, 17 );
QCOMPARE( getStringList( m, key ), QStringList {} ); QCOMPARE( getStringList( m, key ), QStringList {} );
m.insert( key, QString( "more strings" ) );
QCOMPARE( getStringList( m, key ),
QStringList { "more strings" } ); // A single string **can** be considered a stringlist!
m.insert( key, QVariant {} ); m.insert( key, QVariant {} );
QCOMPARE( getStringList( m, key ), QStringList {} ); QCOMPARE( getStringList( m, key ), QStringList {} );
} }
{ {
// Things that are stringlists // Things that are **like** stringlists
QVariantMap m;
m.insert( key, QString( "astring" ) );
QCOMPARE( getStringList( m, key ).count(), 1 );
QCOMPARE( getStringList( m, key ),
QStringList { "astring" } ); // A single string **can** be considered a stringlist!
m.insert( key, QString( "more strings" ) );
QCOMPARE( getStringList( m, key ).count(), 1 );
QCOMPARE( getStringList( m, key ),
QStringList { "more strings" } );
m.insert( key, QString() );
QCOMPARE( getStringList( m, key ).count(), 1 );
QCOMPARE( getStringList( m, key ), QStringList { QString() } );
}
{
// Things that are definitely stringlists
QVariantMap m; QVariantMap m;
m.insert( key, QStringList { "aap", "noot" } ); m.insert( key, QStringList { "aap", "noot" } );
QCOMPARE( getStringList( m, key ).count(), 2 );
QVERIFY( getStringList( m, key ).contains( "aap" ) ); QVERIFY( getStringList( m, key ).contains( "aap" ) );
QVERIFY( !getStringList( m, key ).contains( "mies" ) ); QVERIFY( !getStringList( m, key ).contains( "mies" ) );
} }

View File

@ -25,13 +25,17 @@ namespace CalamaresUtils
*/ */
DLLEXPORT bool getBool( const QVariantMap& map, const QString& key, bool d = false ); DLLEXPORT bool getBool( const QVariantMap& map, const QString& key, bool d = false );
/** /** @brief Get a string value from a mapping with a given key; returns @p d if no value.
* Get a string value from a mapping with a given key; returns @p d if no value. *
* The value must be an actual string; numbers are not automatically converted to strings,
* nor are lists flattened or converted.
*/ */
DLLEXPORT QString getString( const QVariantMap& map, const QString& key, const QString& d = QString() ); DLLEXPORT QString getString( const QVariantMap& map, const QString& key, const QString& d = QString() );
/** /** @brief Get a string list from a mapping with a given key; returns @p d if no value.
* Get a string list from a mapping with a given key; returns @p d if no value. *
* This is slightly more lenient that getString(), and a single-string value will
* be returned as a 1-item list.
*/ */
DLLEXPORT QStringList getStringList( const QVariantMap& map, const QString& key, const QStringList& d = QStringList() ); DLLEXPORT QStringList getStringList( const QVariantMap& map, const QString& key, const QStringList& d = QStringList() );

View File

@ -613,4 +613,10 @@ ViewManager::isSetupMode() const
return s ? s->isSetupMode() : false; return s ? s->isSetupMode() : false;
} }
QString
ViewManager::logFilePath() const
{
return Logger::logFile();
}
} // namespace Calamares } // namespace Calamares

View File

@ -54,6 +54,7 @@ class UIDLLEXPORT ViewManager : public QAbstractListModel
Q_PROPERTY( bool isDebugMode READ isDebugMode CONSTANT FINAL ) Q_PROPERTY( bool isDebugMode READ isDebugMode CONSTANT FINAL )
Q_PROPERTY( bool isChrootMode READ isChrootMode CONSTANT FINAL ) Q_PROPERTY( bool isChrootMode READ isChrootMode CONSTANT FINAL )
Q_PROPERTY( bool isSetupMode READ isSetupMode CONSTANT FINAL ) Q_PROPERTY( bool isSetupMode READ isSetupMode CONSTANT FINAL )
Q_PROPERTY( QString logFilePath READ logFilePath CONSTANT FINAL )
public: public:
/** /**
@ -208,6 +209,8 @@ public Q_SLOTS:
bool isChrootMode() const; bool isChrootMode() const;
/// @brief Proxy to Settings::isSetupMode() default @c false /// @brief Proxy to Settings::isSetupMode() default @c false
bool isSetupMode() const; bool isSetupMode() const;
/// @brief Proxy to Logger::logFile(), default empty
QString logFilePath() const;
signals: signals:
void currentStepChanged(); void currentStepChanged();

View File

@ -281,6 +281,33 @@ def install_systemd_boot(efi_directory):
create_loader(loader_path, distribution_translated) create_loader(loader_path, distribution_translated)
def get_grub_efi_parameters():
"""
Returns a 3-tuple of suitable parameters for GRUB EFI installation,
depending on the host machine architecture. The return is
- target name
- grub.efi name
- boot.efi name
all three are strings. May return None if there is no suitable
set for the current machine. May return unsuitable values if the
host architecture is unknown (e.g. defaults to x86_64).
"""
import platform
efi_bitness = efi_word_size()
cpu_type = platform.machine()
if efi_bitness == "32":
# Assume all 32-bitters are legacy x86
return ("i386-efi", "grubia32.efi", "bootia32.efi")
elif efi_bitness == "64" and cpu_type == "aarch64":
return ("arm64-efi", "grubaa64.efi", "bootaa64.efi")
elif efi_bitness == "64":
# If it's not ARM, must by AMD64
return ("x86_64-efi", "grubx64.efi", "bootx64.efi")
libcalamares.utils.warning("Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type)))
return None
def install_grub(efi_directory, fw_type): def install_grub(efi_directory, fw_type):
""" """
Installs grub as bootloader, either in pc or efi mode. Installs grub as bootloader, either in pc or efi mode.
@ -297,16 +324,8 @@ def install_grub(efi_directory, fw_type):
os.makedirs(install_efi_directory) os.makedirs(install_efi_directory)
efi_bootloader_id = efi_label() efi_bootloader_id = efi_label()
efi_bitness = efi_word_size()
if efi_bitness == "32": efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters()
efi_target = "i386-efi"
efi_grub_file = "grubia32.efi"
efi_boot_file = "bootia32.efi"
elif efi_bitness == "64":
efi_target = "x86_64-efi"
efi_grub_file = "grubx64.efi"
efi_boot_file = "bootx64.efi"
check_target_env_call([libcalamares.job.configuration["grubInstall"], check_target_env_call([libcalamares.job.configuration["grubInstall"],
"--target=" + efi_target, "--target=" + efi_target,

View File

@ -7,3 +7,5 @@
ru us - ruwin_alt_sh-UTF-8 ru us - ruwin_alt_sh-UTF-8
ua us - ua-utf ua us - ua-utf
gr us - gr gr us - gr
he us - he
ar us - ar

View File

@ -168,7 +168,7 @@ Column {
hoverEnabled: true hoverEnabled: true
property var coordinate: map.toCoordinate(Qt.point(mouseX, mouseY)) property var coordinate: map.toCoordinate(Qt.point(mouseX, mouseY))
Label { Label {
x: parent.mouseX - width x: parent.mouseX - width -5
y: parent.mouseY - height - 5 y: parent.mouseY - height - 5
text: "%1, %2".arg( text: "%1, %2".arg(
parent.coordinate.latitude).arg(parent.coordinate.longitude) parent.coordinate.latitude).arg(parent.coordinate.longitude)

View File

@ -72,7 +72,7 @@ public:
*/ */
void updateGlobalStorage( const QStringList& selected ) const; void updateGlobalStorage( const QStringList& selected ) const;
/// As updateGlobalStorage() with an empty selection list /// As updateGlobalStorage() with an empty selection list
void updateGlobalStorage() const { updateGlobalStorage( QStringList() ); } void fillGSSecondaryConfiguration() const { updateGlobalStorage( QStringList() ); }
private: private:
PackageListModel* m_model = nullptr; PackageListModel* m_model = nullptr;

View File

@ -49,9 +49,11 @@ if ( KPMcore_FOUND AND Qt5DBus_FOUND AND KF5CoreAddons_FOUND AND KF5Config_FOUND
TYPE viewmodule TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES SOURCES
Config.cpp
PartitionViewStep.cpp
core/BootLoaderModel.cpp core/BootLoaderModel.cpp
core/ColorUtils.cpp core/ColorUtils.cpp
core/Config.cpp
core/DeviceList.cpp core/DeviceList.cpp
core/DeviceModel.cpp core/DeviceModel.cpp
core/KPMHelpers.cpp core/KPMHelpers.cpp
@ -75,7 +77,6 @@ if ( KPMcore_FOUND AND Qt5DBus_FOUND AND KF5CoreAddons_FOUND AND KF5Config_FOUND
gui/PartitionLabelsView.cpp gui/PartitionLabelsView.cpp
gui/PartitionSizeController.cpp gui/PartitionSizeController.cpp
gui/PartitionSplitterWidget.cpp gui/PartitionSplitterWidget.cpp
gui/PartitionViewStep.cpp
gui/ResizeVolumeGroupDialog.cpp gui/ResizeVolumeGroupDialog.cpp
gui/ScanningDialog.cpp gui/ScanningDialog.cpp
gui/ReplaceWidget.cpp gui/ReplaceWidget.cpp

View File

@ -9,6 +9,8 @@
#include "Config.h" #include "Config.h"
#include "core/PartUtils.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@ -180,7 +182,7 @@ Config::setInstallChoice( InstallChoice c )
if ( c != m_installChoice ) if ( c != m_installChoice )
{ {
m_installChoice = c; m_installChoice = c;
emit installChoiceChanged( c ); Q_EMIT installChoiceChanged( c );
::updateGlobalStorage( c, m_swapChoice ); ::updateGlobalStorage( c, m_swapChoice );
} }
} }
@ -202,16 +204,98 @@ Config::setSwapChoice( Config::SwapChoice c )
if ( c != m_swapChoice ) if ( c != m_swapChoice )
{ {
m_swapChoice = c; m_swapChoice = c;
emit swapChoiceChanged( c ); Q_EMIT swapChoiceChanged( c );
::updateGlobalStorage( m_installChoice, c ); ::updateGlobalStorage( m_installChoice, c );
} }
} }
bool void
Config::allowManualPartitioning() const Config::setEraseFsTypeChoice( const QString& choice )
{
QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr );
if ( canonicalChoice != m_eraseFsTypeChoice )
{
m_eraseFsTypeChoice = canonicalChoice;
Q_EMIT eraseModeFilesystemChanged( canonicalChoice );
}
}
static void
fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configurationMap )
{
// Set up firmwareType global storage entry. This is used, e.g. by the bootloader module.
QString firmwareType( PartUtils::isEfiSystem() ? QStringLiteral( "efi" ) : QStringLiteral( "bios" ) );
gs->insert( "firmwareType", firmwareType );
gs->insert( "efiSystemPartition", CalamaresUtils::getString( configurationMap, "efiSystemPartition", QStringLiteral( "/boot/efi" ) ) );
// Read and parse key efiSystemPartitionSize
if ( configurationMap.contains( "efiSystemPartitionSize" ) )
{
gs->insert( "efiSystemPartitionSize", CalamaresUtils::getString( configurationMap, "efiSystemPartitionSize" ) );
}
// Read and parse key efiSystemPartitionName
if ( configurationMap.contains( "efiSystemPartitionName" ) )
{
gs->insert( "efiSystemPartitionName", CalamaresUtils::getString( configurationMap, "efiSystemPartitionName" ) );
}
}
void
Config::fillConfigurationFSTypes(const QVariantMap& configurationMap)
{ {
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
return gs->value( "allowManualPartitioning" ).toBool();
// The defaultFileSystemType setting needs a bit more processing,
// as we want to cover various cases (such as different cases)
QString fsName = CalamaresUtils::getString( configurationMap, "defaultFileSystemType" );
QString fsRealName;
FileSystem::Type fsType = FileSystem::Type::Unknown;
if ( fsName.isEmpty() )
{
cWarning() << "Partition-module setting *defaultFileSystemType* is missing, will use ext4";
fsRealName = PartUtils::canonicalFilesystemName( QStringLiteral("ext4"), &fsType );
}
else
{
fsRealName = PartUtils::canonicalFilesystemName( fsName, &fsType );
if ( fsType == FileSystem::Type::Unknown )
{
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << fsName << ") using ext4 instead";
fsRealName = PartUtils::canonicalFilesystemName( QStringLiteral("ext4"), &fsType );
}
else if ( fsRealName != fsName )
{
cWarning() << "Partition-module setting *defaultFileSystemType* changed to" << fsRealName;
}
}
Q_ASSERT( fsType != FileSystem::Type::Unknown );
m_defaultFsType = fsType;
gs->insert( "defaultFileSystemType", fsRealName );
// TODO: canonicalize the names? How is translation supposed to work?
m_eraseFsTypes = CalamaresUtils::getStringList( configurationMap, "availableFileSystemTypes" );
if ( !m_eraseFsTypes.contains( fsRealName ) )
{
if ( !m_eraseFsTypes.isEmpty() )
{
// Explicitly set, and doesn't include the default
cWarning() << "Partition-module *availableFileSystemTypes* does not contain the default" << fsRealName;
m_eraseFsTypes.prepend( fsRealName );
}
else
{
// Not explicitly set, so it's empty; don't complain
m_eraseFsTypes = QStringList { fsRealName };
}
}
Q_ASSERT( !m_eraseFsTypes.isEmpty() );
Q_ASSERT( m_eraseFsTypes.contains( fsRealName ) );
m_eraseFsTypeChoice = fsRealName;
Q_EMIT eraseModeFilesystemChanged( m_eraseFsTypeChoice );
} }
@ -236,27 +320,18 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
} }
setSwapChoice( m_initialSwapChoice ); setSwapChoice( m_initialSwapChoice );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); m_allowManualPartitioning = CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true );
gs->insert( "allowManualPartitioning",
CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ) );
if ( configurationMap.contains( "requiredPartitionTableType" ) Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
&& configurationMap.value( "requiredPartitionTableType" ).type() == QVariant::List ) m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" );
{
m_requiredPartitionTableType.clear();
m_requiredPartitionTableType.append( configurationMap.value( "requiredPartitionTableType" ).toStringList() );
}
else if ( configurationMap.contains( "requiredPartitionTableType" )
&& configurationMap.value( "requiredPartitionTableType" ).type() == QVariant::String )
{
m_requiredPartitionTableType.clear();
m_requiredPartitionTableType.append( configurationMap.value( "requiredPartitionTableType" ).toString() );
}
gs->insert( "requiredPartitionTableType", m_requiredPartitionTableType ); gs->insert( "requiredPartitionTableType", m_requiredPartitionTableType );
fillGSConfigurationEFI(gs, configurationMap);
fillConfigurationFSTypes( configurationMap );
} }
void void
Config::updateGlobalStorage() const Config::fillGSSecondaryConfiguration() const
{ {
// If there's no setting (e.g. from the welcome page) for required storage // If there's no setting (e.g. from the welcome page) for required storage
// then use ours, if it was set. // then use ours, if it was set.

View File

@ -12,6 +12,8 @@
#include "utils/NamedEnum.h" #include "utils/NamedEnum.h"
#include <kpmcore/fs/filesystem.h>
#include <QObject> #include <QObject>
#include <QSet> #include <QSet>
@ -24,6 +26,9 @@ class Config : public QObject
///@brief The swap choice (None, Small, Hibernate, ...) which only makes sense when Erase is chosen ///@brief The swap choice (None, Small, Hibernate, ...) which only makes sense when Erase is chosen
Q_PROPERTY( SwapChoice swapChoice READ swapChoice WRITE setSwapChoice NOTIFY swapChoiceChanged ) Q_PROPERTY( SwapChoice swapChoice READ swapChoice WRITE setSwapChoice NOTIFY swapChoiceChanged )
///@brief Name of the FS that will be used when erasing type disk (e.g. "default filesystem")
Q_PROPERTY( QString eraseModeFilesystem READ eraseFsType WRITE setEraseFsTypeChoice NOTIFY eraseModeFilesystemChanged )
Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL ) Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL )
public: public:
@ -54,8 +59,19 @@ public:
static const NamedEnumTable< SwapChoice >& swapChoiceNames(); static const NamedEnumTable< SwapChoice >& swapChoiceNames();
using SwapChoiceSet = QSet< SwapChoice >; using SwapChoiceSet = QSet< SwapChoice >;
using EraseFsTypesSet = QStringList;
void setConfigurationMap( const QVariantMap& ); void setConfigurationMap( const QVariantMap& );
void updateGlobalStorage() const; /** @brief Set GS values where other modules configuration has priority
*
* Some "required" values are duplicated between modules; if some
* othe module hasn't already set the GS value, take a value from
* the partitioning configuration.
*
* Applicable GS keys:
* - requiredStorageGiB
*/
void fillGSSecondaryConfiguration() const;
/** @brief What kind of installation (partitioning) is requested **initially**? /** @brief What kind of installation (partitioning) is requested **initially**?
* *
@ -94,20 +110,44 @@ public:
*/ */
SwapChoice swapChoice() const { return m_swapChoice; } SwapChoice swapChoice() const { return m_swapChoice; }
///@brief Is manual partitioning allowed (not explicitly disnabled in the config file)? /** @brief Get the list of configured FS types to use with *erase* mode
bool allowManualPartitioning() const; *
* This list is not empty.
*/
EraseFsTypesSet eraseFsTypes() const { return m_eraseFsTypes; }
/** @brief Currently-selected FS type for *erase* mode
*/
QString eraseFsType() const { return m_eraseFsTypeChoice; }
/** @brief Configured default FS type (for other modes than erase)
*
* This is not "Unknown" or "Unformatted"
*/
FileSystem::Type defaultFsType() const { return m_defaultFsType; }
///@brief Is manual partitioning allowed (not explicitly disabled in the config file)?
bool allowManualPartitioning() const { return m_allowManualPartitioning; }
public Q_SLOTS: public Q_SLOTS:
void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice
void setInstallChoice( InstallChoice ); void setInstallChoice( InstallChoice );
void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice
void setSwapChoice( SwapChoice ); void setSwapChoice( SwapChoice );
void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem
Q_SIGNALS: Q_SIGNALS:
void installChoiceChanged( InstallChoice ); void installChoiceChanged( InstallChoice );
void swapChoiceChanged( SwapChoice ); void swapChoiceChanged( SwapChoice );
void eraseModeFilesystemChanged( const QString& );
private: private:
/** @brief Handle FS-type configuration, for erase and default */
void fillConfigurationFSTypes( const QVariantMap& configurationMap );
EraseFsTypesSet m_eraseFsTypes;
QString m_eraseFsTypeChoice;
FileSystem::Type m_defaultFsType;
SwapChoiceSet m_swapChoices; SwapChoiceSet m_swapChoices;
SwapChoice m_initialSwapChoice = NoSwap; SwapChoice m_initialSwapChoice = NoSwap;
SwapChoice m_swapChoice = NoSwap; SwapChoice m_swapChoice = NoSwap;
@ -115,6 +155,8 @@ private:
InstallChoice m_installChoice = NoChoice; InstallChoice m_installChoice = NoChoice;
qreal m_requiredStorageGiB = 0.0; // May duplicate setting in the welcome module qreal m_requiredStorageGiB = 0.0; // May duplicate setting in the welcome module
QStringList m_requiredPartitionTableType; QStringList m_requiredPartitionTableType;
bool m_allowManualPartitioning = true;
}; };
/** @brief Given a set of swap choices, return a sensible value from it. /** @brief Given a set of swap choices, return a sensible value from it.

View File

@ -11,10 +11,10 @@
* *
*/ */
#include "gui/PartitionViewStep.h" #include "PartitionViewStep.h"
#include "Config.h"
#include "core/BootLoaderModel.h" #include "core/BootLoaderModel.h"
#include "core/Config.h"
#include "core/DeviceModel.h" #include "core/DeviceModel.h"
#include "core/PartitionCoreModule.h" #include "core/PartitionCoreModule.h"
#include "gui/ChoicePage.h" #include "gui/ChoicePage.h"
@ -327,7 +327,7 @@ PartitionViewStep::isNextEnabled() const
void void
PartitionViewStep::nextPossiblyChanged( bool ) PartitionViewStep::nextPossiblyChanged( bool )
{ {
emit nextStatusChanged( isNextEnabled() ); Q_EMIT nextStatusChanged( isNextEnabled() );
} }
bool bool
@ -368,7 +368,7 @@ PartitionViewStep::isAtEnd() const
void void
PartitionViewStep::onActivate() PartitionViewStep::onActivate()
{ {
m_config->updateGlobalStorage(); m_config->fillGSSecondaryConfiguration();
// if we're coming back to PVS from the next VS // if we're coming back to PVS from the next VS
if ( m_widget->currentWidget() == m_choicePage && m_config->installChoice() == Config::InstallChoice::Alongside ) if ( m_widget->currentWidget() == m_choicePage && m_config->installChoice() == Config::InstallChoice::Alongside )
@ -403,8 +403,7 @@ shouldWarnForGPTOnBIOS( const PartitionCoreModule* core )
&& ( partition->fileSystem().type() == FileSystem::Unformatted ) && ( partition->fileSystem().type() == FileSystem::Unformatted )
&& ( partition->capacity() >= 8_MiB ) ) && ( partition->capacity() >= 8_MiB ) )
{ {
cDebug() << Logger::SubEntry << "Partition" << partition->devicePath() cDebug() << Logger::SubEntry << "Partition" << partition->devicePath() << partition->partitionPath()
<< partition->partitionPath()
<< "is a suitable bios_grub partition"; << "is a suitable bios_grub partition";
return false; return false;
} }
@ -542,32 +541,11 @@ PartitionViewStep::onLeave()
void void
PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap ) PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
Logger::Once o;
m_config->setConfigurationMap( configurationMap ); m_config->setConfigurationMap( 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();
QString efiSP = CalamaresUtils::getString( configurationMap, "efiSystemPartition", QStringLiteral( "/boot/efi" ) );
gs->insert( "efiSystemPartition", efiSP );
// Set up firmwareType global storage entry. This is used, e.g. by the bootloader module.
QString firmwareType( PartUtils::isEfiSystem() ? QStringLiteral( "efi" ) : QStringLiteral( "bios" ) );
cDebug() << o << "Setting firmwareType to" << firmwareType;
gs->insert( "firmwareType", firmwareType );
// Read and parse key efiSystemPartitionSize
if ( configurationMap.contains( "efiSystemPartitionSize" ) )
{
gs->insert( "efiSystemPartitionSize", CalamaresUtils::getString( configurationMap, "efiSystemPartitionSize" ) );
}
// Read and parse key efiSystemPartitionName
if ( configurationMap.contains( "efiSystemPartitionName" ) )
{
gs->insert( "efiSystemPartitionName", CalamaresUtils::getString( configurationMap, "efiSystemPartitionName" ) );
}
// Read and parse key swapPartitionName // Read and parse key swapPartitionName
if ( configurationMap.contains( "swapPartitionName" ) ) if ( configurationMap.contains( "swapPartitionName" ) )
@ -583,30 +561,6 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
gs->insert( "enableLuksAutomatedPartitioning", gs->insert( "enableLuksAutomatedPartitioning",
CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) ); CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) );
// The defaultFileSystemType setting needs a bit more processing,
// as we want to cover various cases (such as different cases)
QString fsName = CalamaresUtils::getString( configurationMap, "defaultFileSystemType" );
FileSystem::Type fsType;
if ( fsName.isEmpty() )
{
cWarning() << "Partition-module setting *defaultFileSystemType* is missing, will use ext4";
}
QString fsRealName = PartUtils::findFS( fsName, &fsType );
if ( fsRealName == fsName )
{
cDebug() << o << "Partition-module setting *defaultFileSystemType*" << fsRealName;
}
else if ( fsType != FileSystem::Unknown )
{
cWarning() << "Partition-module setting *defaultFileSystemType* changed" << fsRealName;
}
else
{
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << fsName << ") using" << fsRealName
<< "instead.";
}
gs->insert( "defaultFileSystemType", fsRealName );
QString partitionTableName = CalamaresUtils::getString( configurationMap, "defaultPartitionTableType" ); QString partitionTableName = CalamaresUtils::getString( configurationMap, "defaultPartitionTableType" );
if ( partitionTableName.isEmpty() ) if ( partitionTableName.isEmpty() )
{ {
@ -628,7 +582,7 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
QFuture< void > future = QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule ); QFuture< void > future = QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
m_future->setFuture( future ); m_future->setFuture( future );
m_core->initLayout( fsType == FileSystem::Unknown ? FileSystem::Ext4 : fsType, m_core->initLayout( m_config->defaultFsType(),
configurationMap.value( "partitionLayout" ).toList() ); configurationMap.value( "partitionLayout" ).toList() );
} }

View File

@ -10,8 +10,8 @@
#include "DeviceList.h" #include "DeviceList.h"
#include "utils/Logger.h"
#include "partition/PartitionIterator.h" #include "partition/PartitionIterator.h"
#include "utils/Logger.h"
#include <kpmcore/backend/corebackend.h> #include <kpmcore/backend/corebackend.h>
#include <kpmcore/backend/corebackendmanager.h> #include <kpmcore/backend/corebackendmanager.h>

View File

@ -132,7 +132,7 @@ DeviceModel::swapDevice( Device* oldDevice, Device* newDevice )
m_devices[ indexOfOldDevice ] = newDevice; m_devices[ indexOfOldDevice ] = newDevice;
emit dataChanged( index( indexOfOldDevice ), index( indexOfOldDevice ) ); Q_EMIT dataChanged( index( indexOfOldDevice ), index( indexOfOldDevice ) );
} }
void void

View File

@ -22,6 +22,7 @@
#include "partition/PartitionQuery.h" #include "partition/PartitionQuery.h"
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/RAII.h"
#include <kpmcore/backend/corebackend.h> #include <kpmcore/backend/corebackend.h>
#include <kpmcore/backend/corebackendmanager.h> #include <kpmcore/backend/corebackendmanager.h>
@ -177,7 +178,8 @@ canBeResized( Partition* candidate, const Logger::Once& o )
if ( availableStorageB > advisedStorageB ) if ( availableStorageB > advisedStorageB )
{ {
cDebug() << o << "Partition" << convenienceName( candidate ) << "authorized for resize + autopartition install."; cDebug() << o << "Partition" << convenienceName( candidate )
<< "authorized for resize + autopartition install.";
return true; return true;
} }
else else
@ -412,8 +414,14 @@ runOsprober( DeviceModel* dm )
FstabEntryList fstabEntries = lookForFstabEntries( path ); FstabEntryList fstabEntries = lookForFstabEntries( path );
QString homePath = findPartitionPathForMountPoint( fstabEntries, "/home" ); QString homePath = findPartitionPathForMountPoint( fstabEntries, "/home" );
osproberEntries.append( osproberEntries.append( { prettyName,
{ prettyName, path, file, QString(), canBeResized( dm, path, o ), lineColumns, fstabEntries, homePath } ); path,
file,
QString(),
canBeResized( dm, path, o ),
lineColumns,
fstabEntries,
homePath } );
osproberCleanLines.append( line ); osproberCleanLines.append( line );
} }
} }
@ -472,21 +480,19 @@ isEfiBootable( const Partition* candidate )
} }
QString QString
findFS( QString fsName, FileSystem::Type* fsType ) canonicalFilesystemName( const QString& fsName, FileSystem::Type* fsType )
{ {
QStringList fsLanguage { QLatin1String( "C" ) }; // Required language list to turn off localization cPointerSetter type( fsType );
if ( fsName.isEmpty() ) if ( fsName.isEmpty() )
{ {
fsName = QStringLiteral( "ext4" ); type = FileSystem::Ext4;
return QStringLiteral( "ext4" );
} }
FileSystem::Type tmpType = FileSystem::typeForName( fsName, fsLanguage ); QStringList fsLanguage { QLatin1String( "C" ) }; // Required language list to turn off localization
if ( tmpType != FileSystem::Unknown )
if ( ( type = FileSystem::typeForName( fsName, fsLanguage ) ) != FileSystem::Unknown )
{ {
if ( fsType )
{
*fsType = tmpType;
}
return fsName; return fsName;
} }
@ -506,7 +512,6 @@ findFS( QString fsName, FileSystem::Type* fsType )
} }
cWarning() << "Filesystem" << fsName << "not found, using ext4"; cWarning() << "Filesystem" << fsName << "not found, using ext4";
fsName = QStringLiteral( "ext4" );
// fsType can be used to check whether fsName was a valid filesystem. // fsType can be used to check whether fsName was a valid filesystem.
if ( fsType ) if ( fsType )
{ {
@ -526,7 +531,8 @@ findFS( QString fsName, FileSystem::Type* fsType )
} }
} }
#endif #endif
return fsName; type = FileSystem::Unknown;
return QStringLiteral( "ext4" );
} }
} // namespace PartUtils } // namespace PartUtils

View File

@ -91,11 +91,13 @@ bool isEfiBootable( const Partition* candidate );
/** @brief translate @p fsName into a recognized name and type /** @brief translate @p fsName into a recognized name and type
* *
* Makes several attempts to translate the string into a * Makes several attempts to translate the string into a
* name that KPMCore will recognize. * name that KPMCore will recognize. Returns the canonical
* filesystem name (e.g. asking for "EXT4" will return "ext4").
*
* The corresponding filesystem type is stored in @p fsType, and * The corresponding filesystem type is stored in @p fsType, and
* its value is FileSystem::Unknown if @p fsName is not recognized. * its value is FileSystem::Unknown if @p fsName is not recognized.
*/ */
QString findFS( QString fsName, FileSystem::Type* fsType ); QString canonicalFilesystemName( const QString& fsName, FileSystem::Type* fsType );
} // namespace PartUtils } // namespace PartUtils

View File

@ -16,13 +16,12 @@
#include "core/PartitionCoreModule.h" #include "core/PartitionCoreModule.h"
#include "core/PartitionInfo.h" #include "core/PartitionInfo.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/NamedEnum.h"
#include "utils/Units.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/NamedEnum.h"
#include "utils/Units.h"
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
@ -109,6 +108,12 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
partType = isEfi ? PartitionTable::gpt : PartitionTable::msdos; partType = isEfi ? PartitionTable::gpt : PartitionTable::msdos;
} }
// Looking up the defaultFsType (which should name a filesystem type)
// will log an error and set the type to Unknown if there's something wrong.
FileSystem::Type type = FileSystem::Unknown;
PartUtils::canonicalFilesystemName( o.defaultFsType, &type );
core->initLayout( type == FileSystem::Unknown ? FileSystem::Ext4 : type );
core->createPartitionTable( dev, partType ); core->createPartitionTable( dev, partType );
if ( isEfi ) if ( isEfi )

View File

@ -10,7 +10,7 @@
#ifndef PARTITIONACTIONS_H #ifndef PARTITIONACTIONS_H
#define PARTITIONACTIONS_H #define PARTITIONACTIONS_H
#include "core/Config.h" #include "Config.h"
#include <QSet> #include <QSet>
#include <QString> #include <QString>

View File

@ -687,7 +687,7 @@ void
PartitionCoreModule::refreshPartition( Device* device, Partition* ) PartitionCoreModule::refreshPartition( Device* device, Partition* )
{ {
// Keep it simple for now: reset the model. This can be improved to cause // Keep it simple for now: reset the model. This can be improved to cause
// the model to emit dataChanged() for the affected row instead, avoiding // the model to Q_EMIT dataChanged() for the affected row instead, avoiding
// the loss of the current selection. // the loss of the current selection.
auto model = partitionModelForDevice( device ); auto model = partitionModelForDevice( device );
Q_ASSERT( model ); Q_ASSERT( model );
@ -966,7 +966,7 @@ PartitionCoreModule::revert()
m_deviceInfos.clear(); m_deviceInfos.clear();
doInit(); doInit();
updateIsDirty(); updateIsDirty();
emit reverted(); Q_EMIT reverted();
} }
@ -1040,7 +1040,7 @@ PartitionCoreModule::revertDevice( Device* dev, bool individualRevert )
{ {
refreshAfterModelChange(); refreshAfterModelChange();
} }
emit deviceReverted( newDev ); Q_EMIT deviceReverted( newDev );
} }

View File

@ -75,7 +75,7 @@ PartitionLayout::PartitionEntry::PartitionEntry( const QString& label,
, partMinSize( minSize ) , partMinSize( minSize )
, partMaxSize( maxSize ) , partMaxSize( maxSize )
{ {
PartUtils::findFS( fs, &partFileSystem ); PartUtils::canonicalFilesystemName( fs, &partFileSystem );
} }
@ -95,7 +95,7 @@ PartitionLayout::addEntry( const PartitionEntry& entry )
void void
PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& config ) PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& config )
{ {
bool ok; bool ok = true; // bogus argument to getSubMap()
m_partLayout.clear(); m_partLayout.clear();
@ -130,10 +130,71 @@ PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& confi
if ( !m_partLayout.count() ) if ( !m_partLayout.count() )
{ {
addEntry( { defaultFsType, QString( "/" ), QString( "100%" ) } ); // Unknown will be translated to defaultFsType at apply-time
addEntry( { FileSystem::Type::Unknown, QString( "/" ), QString( "100%" ) } );
} }
setDefaultFsType( defaultFsType );
} }
void
PartitionLayout::setDefaultFsType(FileSystem::Type defaultFsType)
{
using T = FileSystem::Type;
switch ( defaultFsType )
{
case T::Unknown:
case T::Unformatted:
case T::Extended:
case T::LinuxSwap:
case T::Luks:
case T::Ocfs2:
case T::Lvm2_PV:
case T::Udf:
case T::Iso9660:
case T::Luks2:
case T::LinuxRaidMember:
case T::BitLocker:
// bad bad
cWarning() << "The selected default FS" << defaultFsType << "is not suitable." << "Using ext4 instead.";
defaultFsType = T::Ext4;
break;
case T::Ext2:
case T::Ext3:
case T::Ext4:
case T::Fat32:
case T::Ntfs:
case T::Reiser4:
case T::ReiserFS:
case T::Xfs:
case T::Jfs:
case T::Btrfs:
case T::Exfat:
case T::F2fs:
// ok
break;
case T::Fat12:
case T::Fat16:
case T::Hfs:
case T::HfsPlus:
case T::Ufs:
case T::Hpfs:
case T::Zfs:
case T::Nilfs2:
case T::Apfs:
case T::Minix:
// weird
cWarning() << "The selected default FS" << defaultFsType << "is unusual, but not wrong.";
break;
default:
cWarning() << "The selected default FS" << defaultFsType << "is not known to Calamares." << "Using ext4 instead.";
defaultFsType = T::Ext4;
}
m_defaultFsType = defaultFsType;
}
QList< Partition* > QList< Partition* >
PartitionLayout::createPartitions( Device* dev, PartitionLayout::createPartitions( Device* dev,
qint64 firstSector, qint64 firstSector,
@ -142,6 +203,9 @@ PartitionLayout::createPartitions( Device* dev,
PartitionNode* parent, PartitionNode* parent,
const PartitionRole& role ) const PartitionRole& role )
{ {
// Make sure the default FS is sensible; warn and use ext4 if not
setDefaultFsType( m_defaultFsType );
QList< Partition* > partList; QList< Partition* > partList;
// Map each partition entry to its requested size (0 when calculated later) // Map each partition entry to its requested size (0 when calculated later)
QMap< const PartitionLayout::PartitionEntry*, qint64 > partSectorsMap; QMap< const PartitionLayout::PartitionEntry*, qint64 > partSectorsMap;
@ -210,6 +274,8 @@ PartitionLayout::createPartitions( Device* dev,
} }
} }
auto correctFS = [d=m_defaultFsType]( FileSystem::Type t ) { return t == FileSystem::Type::Unknown ? d : t; };
// Create the partitions. // Create the partitions.
currentSector = firstSector; currentSector = firstSector;
availableSectors = totalSectors; availableSectors = totalSectors;
@ -229,7 +295,7 @@ PartitionLayout::createPartitions( Device* dev,
part = KPMHelpers::createNewPartition( parent, part = KPMHelpers::createNewPartition( parent,
*dev, *dev,
role, role,
entry.partFileSystem, correctFS( entry.partFileSystem ),
entry.partLabel, entry.partLabel,
currentSector, currentSector,
currentSector + sectors - 1, currentSector + sectors - 1,
@ -240,7 +306,7 @@ PartitionLayout::createPartitions( Device* dev,
part = KPMHelpers::createNewEncryptedPartition( parent, part = KPMHelpers::createNewEncryptedPartition( parent,
*dev, *dev,
role, role,
entry.partFileSystem, correctFS( entry.partFileSystem ),
entry.partLabel, entry.partLabel,
currentSector, currentSector,
currentSector + sectors - 1, currentSector + sectors - 1,

View File

@ -87,11 +87,28 @@ public:
* *
* @p config is a list of partition entries (in QVariant form, * @p config is a list of partition entries (in QVariant form,
* read from YAML). If no entries are given, then a single * read from YAML). If no entries are given, then a single
* partition is created with the given @p defaultFsType * partition is created with type Unkown.
*
* Any partitions with FS type Unknown will get the default filesystem
* that is set at **apply** time (e.g. when createPartitions() is
* called as well.
*
* @see setDefaultFsType()
*/ */
void init( FileSystem::Type defaultFsType, const QVariantList& config ); void init( FileSystem::Type defaultFsType, const QVariantList& config );
/** @brief add an entry as if it had been listed in the config
*
* The same comments about filesystem type apply.
*/
bool addEntry( const PartitionEntry& entry ); bool addEntry( const PartitionEntry& entry );
/** @brief set the default filesystem type
*
* Any partitions in the layout with type Unknown will get
* the default type when createPartitions() is called.
*/
void setDefaultFsType( FileSystem::Type defaultFsType );
/** /**
* @brief Apply the current partition layout to the selected drive space. * @brief Apply the current partition layout to the selected drive space.
* @return A list of Partition objects. * @return A list of Partition objects.
@ -105,6 +122,7 @@ public:
private: private:
QList< PartitionEntry > m_partLayout; QList< PartitionEntry > m_partLayout;
FileSystem::Type m_defaultFsType = FileSystem::Type::Unknown;
}; };
#endif /* PARTITIONLAYOUT_H */ #endif /* PARTITIONLAYOUT_H */

View File

@ -327,5 +327,5 @@ PartitionModel::partitionForIndex( const QModelIndex& index ) const
void void
PartitionModel::update() PartitionModel::update()
{ {
emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) ); Q_EMIT dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
} }

View File

@ -41,7 +41,7 @@ public:
/** /**
* This helper class must be instantiated on the stack *before* making * This helper class must be instantiated on the stack *before* making
* changes to the device represented by this model. It will cause the model * changes to the device represented by this model. It will cause the model
* to emit modelAboutToBeReset() when instantiated and modelReset() when * to Q_EMIT modelAboutToBeReset() when instantiated and modelReset() when
* destructed. * destructed.
*/ */
class ResetHelper class ResetHelper

View File

@ -11,15 +11,9 @@
#include "ChoicePage.h" #include "ChoicePage.h"
#include "BootInfoWidget.h" #include "Config.h"
#include "DeviceInfoWidget.h"
#include "PartitionBarsView.h"
#include "PartitionLabelsView.h"
#include "PartitionSplitterWidget.h"
#include "ReplaceWidget.h"
#include "ScanningDialog.h"
#include "core/BootLoaderModel.h" #include "core/BootLoaderModel.h"
#include "core/Config.h"
#include "core/DeviceModel.h" #include "core/DeviceModel.h"
#include "core/KPMHelpers.h" #include "core/KPMHelpers.h"
#include "core/OsproberEntry.h" #include "core/OsproberEntry.h"
@ -28,6 +22,13 @@
#include "core/PartitionCoreModule.h" #include "core/PartitionCoreModule.h"
#include "core/PartitionInfo.h" #include "core/PartitionInfo.h"
#include "core/PartitionModel.h" #include "core/PartitionModel.h"
#include "gui/BootInfoWidget.h"
#include "gui/DeviceInfoWidget.h"
#include "gui/PartitionBarsView.h"
#include "gui/PartitionLabelsView.h"
#include "gui/PartitionSplitterWidget.h"
#include "gui/ReplaceWidget.h"
#include "gui/ScanningDialog.h"
#include "Branding.h" #include "Branding.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
@ -144,7 +145,8 @@ ChoicePage::~ChoicePage() {}
* this avoids cases where the popup would truncate data being drawn * this avoids cases where the popup would truncate data being drawn
* because the overall box is sized too narrow. * because the overall box is sized too narrow.
*/ */
void setModelToComboBox( QComboBox* box, QAbstractItemModel* model ) void
setModelToComboBox( QComboBox* box, QAbstractItemModel* model )
{ {
box->setModel( model ); box->setModel( model );
if ( model->rowCount() > 0 ) if ( model->rowCount() > 0 )
@ -268,6 +270,15 @@ ChoicePage::setupChoices()
m_eraseButton->addOptionsComboBox( m_eraseSwapChoiceComboBox ); m_eraseButton->addOptionsComboBox( m_eraseSwapChoiceComboBox );
} }
if ( m_config->eraseFsTypes().count() > 1)
{
m_eraseFsTypesChoiceComboBox = new QComboBox;
m_eraseFsTypesChoiceComboBox->addItems(m_config->eraseFsTypes());
connect( m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice );
connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged );
m_eraseButton->addOptionsComboBox( m_eraseFsTypesChoiceComboBox );
}
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 );
@ -292,7 +303,7 @@ ChoicePage::setupChoices()
m_config->setInstallChoice( id ); m_config->setInstallChoice( id );
updateNextEnabled(); updateNextEnabled();
emit actionChosen(); Q_EMIT actionChosen();
} }
else // An action was unpicked, either on its own or because of another selection. else // An action was unpicked, either on its own or because of another selection.
{ {
@ -302,7 +313,7 @@ ChoicePage::setupChoices()
m_config->setInstallChoice( InstallChoice::NoChoice ); m_config->setInstallChoice( InstallChoice::NoChoice );
updateNextEnabled(); updateNextEnabled();
emit actionChosen(); Q_EMIT actionChosen();
} }
} }
} ); } );
@ -425,8 +436,8 @@ ChoicePage::continueApplyDeviceChoice()
checkInstallChoiceRadioButton( m_config->installChoice() ); checkInstallChoiceRadioButton( m_config->installChoice() );
} }
emit actionChosen(); Q_EMIT actionChosen();
emit deviceChosen(); Q_EMIT deviceChosen();
} }
@ -464,9 +475,8 @@ ChoicePage::applyActionChoice( InstallChoice choice )
case InstallChoice::Erase: case InstallChoice::Erase:
{ {
auto gs = Calamares::JobQueue::instance()->globalStorage(); auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(), PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(),
gs->value( "defaultFileSystemType" ).toString(), m_config->eraseFsType(),
m_encryptWidget->passphrase(), m_encryptWidget->passphrase(),
gs->value( "efiSystemPartition" ).toString(), gs->value( "efiSystemPartition" ).toString(),
CalamaresUtils::GiBtoBytes( CalamaresUtils::GiBtoBytes(
@ -482,14 +492,14 @@ ChoicePage::applyActionChoice( InstallChoice choice )
} ), } ),
[=] { [=] {
PartitionActions::doAutopartition( m_core, selectedDevice(), options ); PartitionActions::doAutopartition( m_core, selectedDevice(), options );
emit deviceChosen(); Q_EMIT deviceChosen();
}, },
this ); this );
} }
else else
{ {
PartitionActions::doAutopartition( m_core, selectedDevice(), options ); PartitionActions::doAutopartition( m_core, selectedDevice(), options );
emit deviceChosen(); Q_EMIT deviceChosen();
} }
} }
break; break;
@ -1005,7 +1015,8 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
SelectionFilter filter = []( const QModelIndex& index ) { SelectionFilter filter = []( const QModelIndex& index ) {
return PartUtils::canBeResized( return PartUtils::canBeResized(
static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() ), Logger::Once() ); static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() ),
Logger::Once() );
}; };
m_beforePartitionBarsView->setSelectionFilter( filter ); m_beforePartitionBarsView->setSelectionFilter( filter );
m_beforePartitionLabelsView->setSelectionFilter( filter ); m_beforePartitionLabelsView->setSelectionFilter( filter );
@ -1094,7 +1105,8 @@ ChoicePage::updateActionChoicePreview( InstallChoice choice )
{ {
SelectionFilter filter = []( const QModelIndex& index ) { SelectionFilter filter = []( const QModelIndex& index ) {
return PartUtils::canBeReplaced( return PartUtils::canBeReplaced(
static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() ), Logger::Once() ); static_cast< Partition* >( index.data( PartitionModel::PartitionPtrRole ).value< void* >() ),
Logger::Once() );
}; };
m_beforePartitionBarsView->setSelectionFilter( filter ); m_beforePartitionBarsView->setSelectionFilter( filter );
m_beforePartitionLabelsView->setSelectionFilter( filter ); m_beforePartitionLabelsView->setSelectionFilter( filter );
@ -1592,7 +1604,7 @@ ChoicePage::updateNextEnabled()
if ( enabled != m_nextEnabled ) if ( enabled != m_nextEnabled )
{ {
m_nextEnabled = enabled; m_nextEnabled = enabled;
emit nextStatusChanged( enabled ); Q_EMIT nextStatusChanged( enabled );
} }
} }

View File

@ -15,7 +15,7 @@
#include "ui_ChoicePage.h" #include "ui_ChoicePage.h"
#include "core/Config.h" #include "Config.h"
#include "core/OsproberEntry.h" #include "core/OsproberEntry.h"
#include <QMutex> #include <QMutex>
@ -138,7 +138,9 @@ private:
Calamares::PrettyRadioButton* m_eraseButton; Calamares::PrettyRadioButton* m_eraseButton;
Calamares::PrettyRadioButton* m_replaceButton; Calamares::PrettyRadioButton* m_replaceButton;
Calamares::PrettyRadioButton* m_somethingElseButton; Calamares::PrettyRadioButton* m_somethingElseButton;
QComboBox* m_eraseSwapChoiceComboBox; // UI, see also m_eraseSwapChoice QComboBox* m_eraseSwapChoiceComboBox = nullptr; // UI, see also Config's swap choice
QComboBox* m_eraseFsTypesChoiceComboBox = nullptr; // UI, see also Config's erase-mode FS
DeviceInfoWidget* m_deviceInfoWidget; DeviceInfoWidget* m_deviceInfoWidget;

View File

@ -52,7 +52,6 @@ static QSet< FileSystem::Type > s_unmountableFS( { FileSystem::Unformatted,
CreatePartitionDialog::CreatePartitionDialog( Device* device, CreatePartitionDialog::CreatePartitionDialog( Device* device,
PartitionNode* parentPartition, PartitionNode* parentPartition,
Partition* partition,
const QStringList& usedMountPoints, const QStringList& usedMountPoints,
QWidget* parentWidget ) QWidget* parentWidget )
: QDialog( parentWidget ) : QDialog( parentWidget )
@ -81,9 +80,6 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
m_ui->lvNameLineEdit->setValidator( validator ); m_ui->lvNameLineEdit->setValidator( validator );
} }
standardMountPoints( *( m_ui->mountPointComboBox ),
partition ? PartitionInfo::mountPoint( partition ) : QString() );
if ( device->partitionTable()->type() == PartitionTable::msdos if ( device->partitionTable()->type() == PartitionTable::msdos
|| device->partitionTable()->type() == PartitionTable::msdos_sectorbased ) || device->partitionTable()->type() == PartitionTable::msdos_sectorbased )
{ {
@ -96,7 +92,7 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
// File system; the config value is translated (best-effort) to a type // File system; the config value is translated (best-effort) to a type
FileSystem::Type defaultFSType; FileSystem::Type defaultFSType;
QString untranslatedFSName = PartUtils::findFS( QString untranslatedFSName = PartUtils::canonicalFilesystemName(
Calamares::JobQueue::instance()->globalStorage()->value( "defaultFileSystemType" ).toString(), &defaultFSType ); Calamares::JobQueue::instance()->globalStorage()->value( "defaultFileSystemType" ).toString(), &defaultFSType );
if ( defaultFSType == FileSystem::Type::Unknown ) if ( defaultFSType == FileSystem::Type::Unknown )
{ {
@ -132,13 +128,47 @@ CreatePartitionDialog::CreatePartitionDialog( Device* device,
// Select a default // Select a default
m_ui->fsComboBox->setCurrentIndex( defaultFsIndex ); m_ui->fsComboBox->setCurrentIndex( defaultFsIndex );
updateMountPointUi(); updateMountPointUi();
checkMountPointSelection();
}
CreatePartitionDialog::CreatePartitionDialog( Device* device,
const FreeSpace& freeSpacePartition,
const QStringList& usedMountPoints,
QWidget* parentWidget )
: CreatePartitionDialog( device, freeSpacePartition.p->parent(), usedMountPoints, parentWidget )
{
standardMountPoints( *( m_ui->mountPointComboBox ), QString() );
setFlagList( *( m_ui->m_listFlags ), setFlagList( *( m_ui->m_listFlags ),
static_cast< PartitionTable::Flags >( ~PartitionTable::Flags::Int( 0 ) ), static_cast< PartitionTable::Flags >( ~PartitionTable::Flags::Int( 0 ) ),
partition ? PartitionInfo::flags( partition ) : PartitionTable::Flags() ); PartitionTable::Flags() );
initPartResizerWidget( freeSpacePartition.p );
}
// Checks the initial selection. CreatePartitionDialog::CreatePartitionDialog( Device* device,
checkMountPointSelection(); const FreshPartition& existingNewPartition,
const QStringList& usedMountPoints,
QWidget* parentWidget )
: CreatePartitionDialog( device, existingNewPartition.p->parent(), usedMountPoints, parentWidget )
{
standardMountPoints( *( m_ui->mountPointComboBox ), PartitionInfo::mountPoint( existingNewPartition.p ) );
setFlagList( *( m_ui->m_listFlags ),
static_cast< PartitionTable::Flags >( ~PartitionTable::Flags::Int( 0 ) ),
PartitionInfo::flags( existingNewPartition.p ) );
const bool isExtended = existingNewPartition.p->roles().has( PartitionRole::Extended );
if ( isExtended )
{
cDebug() << "Editing extended partitions is not supported.";
return;
}
initPartResizerWidget( existingNewPartition.p );
FileSystem::Type fsType = existingNewPartition.p->fileSystem().type();
m_ui->fsComboBox->setCurrentText( FileSystem::nameForType( fsType ) );
setSelectedMountPoint( m_ui->mountPointComboBox, PartitionInfo::mountPoint( existingNewPartition.p ) );
updateMountPointUi();
} }
CreatePartitionDialog::~CreatePartitionDialog() {} CreatePartitionDialog::~CreatePartitionDialog() {}
@ -188,7 +218,7 @@ CreatePartitionDialog::initGptPartitionTypeUi()
} }
Partition* Partition*
CreatePartitionDialog::createPartition() CreatePartitionDialog::getNewlyCreatedPartition()
{ {
if ( m_role.roles() == PartitionRole::None ) if ( m_role.roles() == PartitionRole::None )
{ {
@ -204,17 +234,21 @@ CreatePartitionDialog::createPartition()
: FileSystem::typeForName( m_ui->fsComboBox->currentText() ); : FileSystem::typeForName( m_ui->fsComboBox->currentText() );
const QString fsLabel = m_ui->filesystemLabelEdit->text(); const QString fsLabel = m_ui->filesystemLabelEdit->text();
// The newly-created partitions have no flags set (no **active** flags),
// because they're new. The desired flags can be retrieved from
// newFlags() and the consumer (see PartitionPage::onCreateClicked)
// does so, to set up the partition for create-and-then-set-flags.
Partition* partition = nullptr; Partition* partition = nullptr;
QString luksPassphrase = m_ui->encryptWidget->passphrase(); QString luksPassphrase = m_ui->encryptWidget->passphrase();
if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty() ) if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty() )
{ {
partition = KPMHelpers::createNewEncryptedPartition( partition = KPMHelpers::createNewEncryptedPartition(
m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, newFlags() ); m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() );
} }
else else
{ {
partition partition = KPMHelpers::createNewPartition(
= KPMHelpers::createNewPartition( m_parent, *m_device, m_role, fsType, fsLabel, first, last, newFlags() ); m_parent, *m_device, m_role, fsType, fsLabel, first, last, PartitionTable::Flags() );
} }
if ( m_device->type() == Device::Type::LVM_Device ) if ( m_device->type() == Device::Type::LVM_Device )
@ -284,34 +318,3 @@ CreatePartitionDialog::initPartResizerWidget( Partition* partition )
m_partitionSizeController->setPartResizerWidget( m_ui->partResizerWidget ); m_partitionSizeController->setPartResizerWidget( m_ui->partResizerWidget );
m_partitionSizeController->setSpinBox( m_ui->sizeSpinBox ); m_partitionSizeController->setSpinBox( m_ui->sizeSpinBox );
} }
void
CreatePartitionDialog::initFromFreeSpace( Partition* freeSpacePartition )
{
initPartResizerWidget( freeSpacePartition );
}
void
CreatePartitionDialog::initFromPartitionToCreate( Partition* partition )
{
Q_ASSERT( partition );
bool isExtended = partition->roles().has( PartitionRole::Extended );
Q_ASSERT( !isExtended );
if ( isExtended )
{
cDebug() << "Editing extended partitions is not supported for now";
return;
}
initPartResizerWidget( partition );
// File System
FileSystem::Type fsType = partition->fileSystem().type();
m_ui->fsComboBox->setCurrentText( FileSystem::nameForType( fsType ) );
// Mount point
setSelectedMountPoint( m_ui->mountPointComboBox, PartitionInfo::mountPoint( partition ) );
updateMountPointUi();
}

View File

@ -33,31 +33,48 @@ class Ui_CreatePartitionDialog;
class CreatePartitionDialog : public QDialog class CreatePartitionDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public:
/** private:
* @brief Dialog for editing a new partition. /** @brief Delegated constructor
* *
* For the (unlikely) case that a newly created partition is being re-edited, * This does all the shared UI setup.
* pass a pointer to that @p partition, otherwise pass nullptr.
*/ */
CreatePartitionDialog( Device* device, CreatePartitionDialog( Device* device,
PartitionNode* parentPartition, PartitionNode* parentPartition,
Partition* partition, const QStringList& usedMountPoints,
QWidget* parentWidget );
public:
struct FreeSpace
{
Partition* p;
};
struct FreshPartition
{
Partition* p;
};
/** @brief Dialog for editing a new partition based on free space.
*
* Creating from free space makes a wholly new partition with
* no flags set at all.
*/
CreatePartitionDialog( Device* device,
const FreeSpace& freeSpacePartition,
const QStringList& usedMountPoints,
QWidget* parentWidget = nullptr );
/** @brief Dialog for editing a newly-created partition.
*
* A partition previously newly created (e.g. via this dialog
* and the constructor above) can be re-edited.
*/
CreatePartitionDialog( Device* device,
const FreshPartition& existingNewPartition,
const QStringList& usedMountPoints, const QStringList& usedMountPoints,
QWidget* parentWidget = nullptr ); QWidget* parentWidget = nullptr );
~CreatePartitionDialog() override; ~CreatePartitionDialog() override;
/** Partition* getNewlyCreatedPartition();
* Must be called when user wants to create a partition in
* freeSpacePartition.
*/
void initFromFreeSpace( Partition* freeSpacePartition );
/**
* Must be called when user wants to edit a to-be-created partition.
*/
void initFromPartitionToCreate( Partition* partition );
Partition* createPartition();
PartitionTable::Flags newFlags() const; PartitionTable::Flags newFlags() const;

View File

@ -97,7 +97,7 @@ EditExistingPartitionDialog::EditExistingPartitionDialog( Device* device,
m_ui->fileSystemComboBox->addItems( fsNames ); m_ui->fileSystemComboBox->addItems( fsNames );
FileSystem::Type defaultFSType; FileSystem::Type defaultFSType;
QString untranslatedFSName = PartUtils::findFS( QString untranslatedFSName = PartUtils::canonicalFilesystemName(
Calamares::JobQueue::instance()->globalStorage()->value( "defaultFileSystemType" ).toString(), &defaultFSType ); Calamares::JobQueue::instance()->globalStorage()->value( "defaultFileSystemType" ).toString(), &defaultFSType );
if ( defaultFSType == FileSystem::Type::Unknown ) if ( defaultFSType == FileSystem::Type::Unknown )
{ {

View File

@ -136,7 +136,7 @@ EncryptWidget::updateState()
if ( newState != m_state ) if ( newState != m_state )
{ {
m_state = newState; m_state = newState;
emit stateChanged( m_state ); Q_EMIT stateChanged( m_state );
} }
} }

View File

@ -23,11 +23,15 @@
QStringList QStringList
standardMountPoints() standardMountPoints()
{ {
QStringList mountPoints { "/", "/boot", "/home", "/opt", "/srv", "/usr", "/var" }; QStringList mountPoints { "/", "/home", "/opt", "/srv", "/usr", "/var" };
if ( PartUtils::isEfiSystem() ) if ( PartUtils::isEfiSystem() )
{ {
mountPoints << Calamares::JobQueue::instance()->globalStorage()->value( "efiSystemPartition" ).toString(); mountPoints << Calamares::JobQueue::instance()->globalStorage()->value( "efiSystemPartition" ).toString();
} }
else
{
mountPoints << QStringLiteral( "/boot" );
}
mountPoints.removeDuplicates(); mountPoints.removeDuplicates();
mountPoints.sort(); mountPoints.sort();
return mountPoints; return mountPoints;
@ -68,11 +72,13 @@ setSelectedMountPoint( QComboBox& combo, const QString& selected )
else else
{ {
for ( int i = 0; i < combo.count(); ++i ) for ( int i = 0; i < combo.count(); ++i )
{
if ( selected == combo.itemText( i ) ) if ( selected == combo.itemText( i ) )
{ {
combo.setCurrentIndex( i ); combo.setCurrentIndex( i );
return; return;
} }
}
combo.addItem( selected ); combo.addItem( selected );
combo.setCurrentIndex( combo.count() - 1 ); combo.setCurrentIndex( combo.count() - 1 );
} }
@ -85,10 +91,12 @@ flagsFromList( const QListWidget& list )
PartitionTable::Flags flags; PartitionTable::Flags flags;
for ( int i = 0; i < list.count(); i++ ) for ( int i = 0; i < list.count(); i++ )
{
if ( list.item( i )->checkState() == Qt::Checked ) if ( list.item( i )->checkState() == Qt::Checked )
{ {
flags |= static_cast< PartitionTable::Flag >( list.item( i )->data( Qt::UserRole ).toInt() ); flags |= static_cast< PartitionTable::Flag >( list.item( i )->data( Qt::UserRole ).toInt() );
} }
}
return flags; return flags;
} }

View File

@ -391,13 +391,14 @@ PartitionPage::onCreateClicked()
return; return;
} }
CreatePartitionDialog dlg( model->device(), partition->parent(), nullptr, getCurrentUsedMountpoints(), this ); QPointer< CreatePartitionDialog > dlg = new CreatePartitionDialog(
dlg.initFromFreeSpace( partition ); model->device(), CreatePartitionDialog::FreeSpace { partition }, getCurrentUsedMountpoints(), this );
if ( dlg.exec() == QDialog::Accepted ) if ( dlg->exec() == QDialog::Accepted )
{ {
Partition* newPart = dlg.createPartition(); Partition* newPart = dlg->getNewlyCreatedPartition();
m_core->createPartition( model->device(), newPart, dlg.newFlags() ); m_core->createPartition( model->device(), newPart, dlg->newFlags() );
} }
delete dlg;
} }
void void
@ -492,11 +493,10 @@ PartitionPage::updatePartitionToCreate( Device* device, Partition* partition )
mountPoints.removeOne( PartitionInfo::mountPoint( partition ) ); mountPoints.removeOne( PartitionInfo::mountPoint( partition ) );
QPointer< CreatePartitionDialog > dlg QPointer< CreatePartitionDialog > dlg
= new CreatePartitionDialog( device, partition->parent(), partition, mountPoints, this ); = new CreatePartitionDialog( device, CreatePartitionDialog::FreshPartition { partition }, mountPoints, this );
dlg->initFromPartitionToCreate( partition );
if ( dlg->exec() == QDialog::Accepted ) if ( dlg->exec() == QDialog::Accepted )
{ {
Partition* newPartition = dlg->createPartition(); Partition* newPartition = dlg->getNewlyCreatedPartition();
m_core->deletePartition( device, partition ); m_core->deletePartition( device, partition );
m_core->createPartition( device, newPartition, dlg->newFlags() ); m_core->createPartition( device, newPartition, dlg->newFlags() );
} }

View File

@ -225,7 +225,7 @@ PartitionSplitterWidget::setSplitPartition( const QString& path, qint64 minSize,
} }
} }
emit partitionResized( m_itemToResize.itemPath, m_itemToResize.size, m_itemToResizeNext.size ); Q_EMIT partitionResized( m_itemToResize.itemPath, m_itemToResize.size, m_itemToResizeNext.size );
cDebug() << "Items updated. Status:"; cDebug() << "Items updated. Status:";
foreach ( const PartitionSplitterItem& item, m_items ) foreach ( const PartitionSplitterItem& item, m_items )
@ -374,7 +374,7 @@ PartitionSplitterWidget::mouseMoveEvent( QMouseEvent* event )
repaint(); repaint();
emit partitionResized( itemPath, m_itemToResize.size, m_itemToResizeNext.size ); Q_EMIT partitionResized( itemPath, m_itemToResize.size, m_itemToResizeNext.size );
} }
else else
{ {

View File

@ -311,7 +311,7 @@ ReplaceWidget::setNextEnabled( bool enabled )
} }
m_nextEnabled = enabled; m_nextEnabled = enabled;
emit nextStatusChanged( enabled ); Q_EMIT nextStatusChanged( enabled );
} }

View File

@ -68,5 +68,5 @@ void
ScanningDialog::setVisible( bool visible ) ScanningDialog::setVisible( bool visible )
{ {
QDialog::setVisible( visible ); QDialog::setVisible( visible );
emit visibilityChanged(); Q_EMIT visibilityChanged();
} }

View File

@ -25,7 +25,7 @@ AutoMountManagementJob::prettyName() const
Calamares::JobResult Calamares::JobResult
AutoMountManagementJob::exec() AutoMountManagementJob::exec()
{ {
Logger::CDebug( Logger::LOGVERBOSE ) << "this" << Logger::Pointer( this ) << "value" << Logger::Pointer( m_stored ) cVerbose() << "this" << Logger::Pointer( this ) << "value" << Logger::Pointer( m_stored )
<< ( m_stored ? "restore" : m_disable ? "disable" : "enable" ); << ( m_stored ? "restore" : m_disable ? "disable" : "enable" );
if ( m_stored ) if ( m_stored )
{ {

View File

@ -43,6 +43,7 @@ ClearTempMountsJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
ClearTempMountsJob::exec() ClearTempMountsJob::exec()
{ {
Logger::Once o;
// Fetch a list of current mounts to Calamares temporary directories. // Fetch a list of current mounts to Calamares temporary directories.
QList< QPair< QString, QString > > lst; QList< QPair< QString, QString > > lst;
QFile mtab( "/etc/mtab" ); QFile mtab( "/etc/mtab" );
@ -51,23 +52,27 @@ ClearTempMountsJob::exec()
return Calamares::JobResult::error( tr( "Cannot get list of temporary mounts." ) ); return Calamares::JobResult::error( tr( "Cannot get list of temporary mounts." ) );
} }
cDebug() << "Opened mtab. Lines:"; cVerbose() << o << "Opened mtab. Lines:";
QTextStream in( &mtab ); QTextStream in( &mtab );
QString lineIn = in.readLine(); QString lineIn = in.readLine();
while ( !lineIn.isNull() ) while ( !lineIn.isNull() )
{ {
QStringList line = lineIn.split( ' ', SplitSkipEmptyParts ); QStringList line = lineIn.split( ' ', SplitSkipEmptyParts );
cDebug() << line.join( ' ' ); cVerbose() << o << line.join( ' ' );
QString device = line.at( 0 ); QString device = line.at( 0 );
QString mountPoint = line.at( 1 ); QString mountPoint = line.at( 1 );
if ( mountPoint.startsWith( "/tmp/calamares-" ) ) if ( mountPoint.startsWith( "/tmp/calamares-" ) )
{ {
cDebug() << "INSERTING pair (device, mountPoint)" << device << mountPoint;
lst.append( qMakePair( device, mountPoint ) ); lst.append( qMakePair( device, mountPoint ) );
} }
lineIn = in.readLine(); lineIn = in.readLine();
} }
if ( lst.empty() )
{
return Calamares::JobResult::ok();
}
std::sort( std::sort(
lst.begin(), lst.end(), []( const QPair< QString, QString >& a, const QPair< QString, QString >& b ) -> bool { lst.begin(), lst.end(), []( const QPair< QString, QString >& a, const QPair< QString, QString >& b ) -> bool {
return a.first > b.first; return a.first > b.first;
@ -76,10 +81,10 @@ ClearTempMountsJob::exec()
QStringList goodNews; QStringList goodNews;
QProcess process; QProcess process;
foreach ( auto line, lst ) for ( const auto& line : qAsConst( lst ) )
{ {
QString partPath = line.second; QString partPath = line.second;
cDebug() << "Will try to umount path" << partPath; cDebug() << o << "Will try to umount path" << partPath;
process.start( "umount", { "-lv", partPath } ); process.start( "umount", { "-lv", partPath } );
process.waitForFinished(); process.waitForFinished();
if ( process.exitCode() == 0 ) if ( process.exitCode() == 0 )
@ -92,7 +97,7 @@ ClearTempMountsJob::exec()
ok.setMessage( tr( "Cleared all temporary mounts." ) ); ok.setMessage( tr( "Cleared all temporary mounts." ) );
ok.setDetails( goodNews.join( "\n" ) ); ok.setDetails( goodNews.join( "\n" ) );
cDebug() << "ClearTempMountsJob finished. Here's what was done:\n" << goodNews.join( "\n" ); cDebug() << o << "ClearTempMountsJob finished. Here's what was done:\n" << Logger::DebugList( goodNews );
return ok; return ok;
} }

View File

@ -60,14 +60,6 @@ CreatePartitionTableJob::prettyStatusMessage() const
} }
static inline QDebug&
operator<<( QDebug&& s, PartitionIterator& it )
{
s << ( ( *it ) ? ( *it )->deviceNode() : QString( "<null device>" ) );
return s;
}
Calamares::JobResult Calamares::JobResult
CreatePartitionTableJob::exec() CreatePartitionTableJob::exec()
{ {
@ -75,13 +67,13 @@ CreatePartitionTableJob::exec()
QString message = tr( "The installer failed to create a partition table on %1." ).arg( m_device->name() ); QString message = tr( "The installer failed to create a partition table on %1." ).arg( m_device->name() );
PartitionTable* table = m_device->partitionTable(); PartitionTable* table = m_device->partitionTable();
cDebug() << "Creating new partition table of type" << table->typeName() << ", uncommitted yet:";
if ( Logger::logLevelEnabled( Logger::LOGDEBUG ) ) if ( Logger::logLevelEnabled( Logger::LOGDEBUG ) )
{ {
cDebug() << "Creating new partition table of type" << table->typeName() << ", uncommitted partitions:";
for ( auto it = PartitionIterator::begin( table ); it != PartitionIterator::end( table ); ++it ) for ( auto it = PartitionIterator::begin( table ); it != PartitionIterator::end( table ); ++it )
{ {
cDebug() << it; cDebug() << Logger::SubEntry << ( ( *it ) ? ( *it )->deviceNode() : QString( "<null device>" ) );
} }
QProcess lsblk; QProcess lsblk;
@ -89,14 +81,14 @@ CreatePartitionTableJob::exec()
lsblk.setProcessChannelMode( QProcess::MergedChannels ); lsblk.setProcessChannelMode( QProcess::MergedChannels );
lsblk.start(); lsblk.start();
lsblk.waitForFinished(); lsblk.waitForFinished();
cDebug() << "lsblk:\n" << lsblk.readAllStandardOutput(); cDebug() << Logger::SubEntry << "lsblk output:\n" << Logger::NoQuote << lsblk.readAllStandardOutput();
QProcess mount; QProcess mount;
mount.setProgram( "mount" ); // Debug output only, not mounting something mount.setProgram( "mount" ); // Debug output only, not mounting something
mount.setProcessChannelMode( QProcess::MergedChannels ); mount.setProcessChannelMode( QProcess::MergedChannels );
mount.start(); mount.start();
mount.waitForFinished(); mount.waitForFinished();
cDebug() << "mount:\n" << mount.readAllStandardOutput(); cDebug() << Logger::SubEntry << "mount output:\n" << Logger::NoQuote << mount.readAllStandardOutput();
} }
CreatePartitionTableOperation op( *m_device, table ); CreatePartitionTableOperation op( *m_device, table );

View File

@ -25,5 +25,5 @@ PartitionJob::iprogress( int percent )
{ {
percent = 100; percent = 100;
} }
emit progress( qreal( percent / 100.0 ) ); Q_EMIT progress( qreal( percent / 100.0 ) );
} }

View File

@ -144,8 +144,9 @@ SetPartFlagsJob::prettyStatusMessage() const
Calamares::JobResult Calamares::JobResult
SetPartFlagsJob::exec() SetPartFlagsJob::exec()
{ {
cDebug() << "Setting flags on" << m_device->deviceNode() << "partition" << partition()->deviceNode() << "to" QStringList flagsList = PartitionTable::flagNames( m_flags );
<< m_flags; cDebug() << "Setting flags on" << m_device->deviceNode() << "partition" << partition()->deviceNode()
<< Logger::DebugList( flagsList );
Report report( nullptr ); Report report( nullptr );
SetPartFlagsOperation op( *m_device, *partition(), m_flags ); SetPartFlagsOperation op( *m_device, *partition(), m_flags );

View File

@ -113,12 +113,11 @@ initialSwapChoice: none
# Restrict the installation on disks that match the type of partition # Restrict the installation on disks that match the type of partition
# tables that are specified. # tables that are specified.
# #
# Suggested values: msdos, gpt # Possible values: msdos, gpt. Names are case-sensitive and defined by KPMCore.
# If nothing is specified, Calamares defaults to both "msdos" and "mbr". #
# If nothing is specified, Calamares defaults to both "msdos" and "gpt".
# #
# Names are case-sensitive and defined by KPMCore.
# requiredPartitionTableType: gpt # requiredPartitionTableType: gpt
# or,
# requiredPartitionTableType: # requiredPartitionTableType:
# - msdos # - msdos
# - gpt # - gpt
@ -139,6 +138,17 @@ initialSwapChoice: none
# Names are case-sensitive and defined by KPMCore. # Names are case-sensitive and defined by KPMCore.
defaultFileSystemType: "ext4" defaultFileSystemType: "ext4"
# Selectable filesystem type, used when "erase" is done.
#
# When erasing the disk, the *defaultFileSystemType* is used (see
# above), but it is also possible to give users a choice:
# list suitable filesystems here. A drop-down is provided
# to pick which is the filesystems will be used.
#
# The value *defaultFileSystemType* is added to this list (with a warning)
# if not present; the default pick is the *defaultFileSystemType*.
availableFileSystemTypes: ["ext4", "btrfs", "f2fs"]
# Show/hide LUKS related functionality in automated partitioning modes. # Show/hide LUKS related functionality in automated partitioning modes.
# Disable this if you choose not to deploy early unlocking support in GRUB2 # Disable this if you choose not to deploy early unlocking support in GRUB2
# and/or your distribution's initramfs solution. # and/or your distribution's initramfs solution.
@ -205,7 +215,12 @@ defaultFileSystemType: "ext4"
# - uuid: partition uuid (optional parameter; gpt only; requires KPMCore >= 4.2.0) # - uuid: partition uuid (optional parameter; gpt only; requires KPMCore >= 4.2.0)
# - type: partition type (optional parameter; gpt only; requires KPMCore >= 4.2.0) # - type: partition type (optional parameter; gpt only; requires KPMCore >= 4.2.0)
# - attributes: partition attributes (optional parameter; gpt only; requires KPMCore >= 4.2.0) # - attributes: partition attributes (optional parameter; gpt only; requires KPMCore >= 4.2.0)
# - filesystem: filesystem type (optional parameter; fs not created if "unformatted" or unset) # - filesystem: filesystem type (optional parameter)
# - if not set at all, treat as "unformatted"
# - if "unformatted", no filesystem will be created
# - if "unknown" (or an unknown FS name, like "elephant") then the
# default filesystem type, or the user's choice, will be applied instead
# of "unknown" (e.g. the user might pick ext4, or xfs).
# - mountPoint: partition mount point (optional parameter; not mounted if unset) # - mountPoint: partition mount point (optional parameter; not mounted if unset)
# - size: partition size in bytes (append 'K', 'M' or 'G' for KiB, MiB or GiB) # - size: partition size in bytes (append 'K', 'M' or 'G' for KiB, MiB or GiB)
# or # or

View File

@ -18,6 +18,8 @@ properties:
alwaysShowPartitionLabels: { type: boolean, default: true } alwaysShowPartitionLabels: { type: boolean, default: true }
defaultFileSystemType: { type: string } defaultFileSystemType: { type: string }
availableFileSystemTypes: { type: array, items: { type: string } }
enableLuksAutomatedPartitioning: { type: boolean, default: false } enableLuksAutomatedPartitioning: { type: boolean, default: false }
allowManualPartitioning: { type: boolean, default: true } allowManualPartitioning: { type: boolean, default: true }
partitionLayout: { type: array } # TODO: specify items partitionLayout: { type: array } # TODO: specify items

View File

@ -545,7 +545,7 @@ Config::passwordStatus( const QString& pw1, const QString& pw2 ) const
} }
} }
return qMakePair( PasswordValidity::Valid, QString() ); return qMakePair( PasswordValidity::Valid, tr( "OK!" ) );
} }

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://calamares.io> === /* === This file is part of Calamares - <https://calamares.io> ===
* *
* SPDX-FileCopyrightText: 2020 Anke Boersma <demm@kaosx.us> * SPDX-FileCopyrightText: 2020 - 2021 Anke Boersma <demm@kaosx.us>
* SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org>
* 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.
@ -10,7 +11,7 @@
import io.calamares.core 1.0 import io.calamares.core 1.0
import io.calamares.ui 1.0 import io.calamares.ui 1.0
import QtQuick 2.10 import QtQuick 2.15
import QtQuick.Controls 2.10 import QtQuick.Controls 2.10
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import org.kde.kirigami 2.7 as Kirigami import org.kde.kirigami 2.7 as Kirigami
@ -26,7 +27,6 @@ Kirigami.ScrollablePage {
Kirigami.Theme.textColor: "#1F1F1F" Kirigami.Theme.textColor: "#1F1F1F"
header: Kirigami.Heading { header: Kirigami.Heading {
Layout.fillWidth: true Layout.fillWidth: true
height: 50 height: 50
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
@ -37,71 +37,63 @@ Kirigami.ScrollablePage {
} }
ColumnLayout { ColumnLayout {
id: _formLayout id: _formLayout
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
Label { Label {
width: parent.width width: parent.width
text: qsTr("What is your name?") text: qsTr("What is your name?")
} }
TextField { TextField {
id: _userNameField id: _userNameField
width: parent.width width: parent.width
enabled: config.isEditable("fullName") enabled: config.isEditable("fullName")
placeholderText: qsTr("Your Full Name") placeholderText: qsTr("Your Full Name")
text: config.fullName text: config.fullName
onTextChanged: config.setFullName(text); onTextChanged: config.setFullName(text)
background: Rectangle { palette.base: _userNameField.text.length ? "#f0fff0" : "#FBFBFB"
color: "#FBFBFB" // Kirigami.Theme.backgroundColor palette.highlight : _userNameField.text.length ? "#dcffdc" : "#FBFBFB"
radius: 2
opacity: 0.9
//border.color: _userNameField.text === "" ? Kirigami.Theme.backgroundColor : ( config.fullNameReady ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor)
border.color: _userNameField.text === "" ? "#FBFBFB" : ( config.fullNameChanged ? "#FBFBFB" : Kirigami.Theme.negativeTextColor)
}
} }
} }
Column { Column {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
Label { Label {
width: parent.width width: parent.width
text: qsTr("What name do you want to use to log in?") text: qsTr("What name do you want to use to log in?")
} }
TextField { TextField {
id: _userLoginField id: _userLoginField
width: parent.width width: parent.width
enabled: config.isEditable("loginName") enabled: config.isEditable("loginName")
placeholderText: qsTr("Login Name") placeholderText: qsTr("Login Name")
text: config.loginName text: config.loginName
onTextChanged: config.setLoginName(text) validator: RegularExpressionValidator { regularExpression: /[a-z_][a-z0-9_-]*[$]?$/ }
background: Rectangle { onTextChanged: acceptableInput
? ( _userLoginField.text === "root"
? forbiddenMessage.visible=true
: ( config.setLoginName(text),
userMessage.visible = false,forbiddenMessage.visible=false ) )
: ( userMessage.visible = true,console.log("Invalid") )
color: "#FBFBFB" // Kirigami.Theme.backgroundColor palette.base: _userLoginField.text.length
opacity: 0.9 ? ( acceptableInput
//border.color: _userLoginField.text === "" ? Kirigami.Theme.backgroundColor : ( config.userNameReady ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor) ? ( _userLoginField.text === "root"
border.color: _userLoginField.text === "" ? "#FBFBFB" : ( config.loginNameStatusChanged ? "#FBFBFB" : Kirigami.Theme.negativeTextColor) ? "#ffdae0" : "#f0fff0" ) : "#ffdae0" ) : "#FBFBFB"
} palette.highlight : _userLoginField.text.length ? "#dcffdc" : "#FBFBFB"
} }
Label { Label {
width: parent.width width: parent.width
text: qsTr("If more than one person will use this computer, you can create multiple accounts after installation.") text: qsTr("If more than one person will use this computer, you can create multiple accounts after installation.")
font.weight: Font.Thin font.weight: Font.Thin
@ -110,36 +102,54 @@ Kirigami.ScrollablePage {
} }
} }
Column { Kirigami.InlineMessage {
id: userMessage
Layout.fillWidth: true
visible: false
type: Kirigami.MessageType.Error
text: qsTr("Only lowercase letters, numbers, underscore and hyphen are allowed.")
}
Kirigami.InlineMessage {
id: forbiddenMessage
Layout.fillWidth: true
visible: false
type: Kirigami.MessageType.Error
text: qsTr("root is not allowed as username.")
}
Column {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
Label { Label {
width: parent.width width: parent.width
text: qsTr("What is the name of this computer?") text: qsTr("What is the name of this computer?")
} }
TextField { TextField {
id: _hostName id: _hostName
width: parent.width width: parent.width
placeholderText: qsTr("Computer Name") placeholderText: qsTr("Computer Name")
text: config.hostName text: config.hostName
onTextChanged: config.setHostName(text) validator: RegularExpressionValidator { regularExpression: /[a-zA-Z0-9][-a-zA-Z0-9_]+/ }
background: Rectangle { onTextChanged: acceptableInput
? ( _hostName.text === "localhost"
? forbiddenHost.visible=true
: ( config.setHostName(text),
hostMessage.visible = false,forbiddenHost.visible = false ) )
: hostMessage.visible = true
color: "#FBFBFB" // Kirigami.Theme.backgroundColor palette.base: _hostName.text.length
opacity: 0.9 ? ( acceptableInput
//border.color: _hostName.text === "" ? Kirigami.Theme.backgroundColor : ( config.hostNameStatusChanged ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor) ? ( _hostName.text === "localhost" ? "#ffdae0" : "#f0fff0" )
border.color: _hostName.text === "" ? "#FBFBFB" : ( config.hostNameStatusChanged ? "#FBFBFB" : Kirigami.Theme.negativeTextColor) : "#ffdae0")
} : "#FBFBFB"
palette.highlight : _hostName.text.length ? "#dcffdc" : "#FBFBFB"
} }
Label { Label {
width: parent.width width: parent.width
text: qsTr("This name will be used if you make the computer visible to others on a network.") text: qsTr("This name will be used if you make the computer visible to others on a network.")
font.weight: Font.Thin font.weight: Font.Thin
@ -148,13 +158,27 @@ Kirigami.ScrollablePage {
} }
} }
Column { Kirigami.InlineMessage {
id: hostMessage
Layout.fillWidth: true
visible: false
type: Kirigami.MessageType.Error
text: qsTr("Only letter, numbers, underscore and hyphen are allowed, minimal of two characters.")
}
Kirigami.InlineMessage {
id: forbiddenHost
Layout.fillWidth: true
visible: false
type: Kirigami.MessageType.Error
text: qsTr("localhost is not allowed as hostname.")
}
Column {
Layout.fillWidth: true Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
Label { Label {
width: parent.width width: parent.width
text: qsTr("Choose a password to keep your account safe.") text: qsTr("Choose a password to keep your account safe.")
} }
@ -164,50 +188,46 @@ Kirigami.ScrollablePage {
spacing: 20 spacing: 20
TextField { TextField {
id: _passwordField id: _passwordField
width: parent.width / 2 - 10 width: parent.width / 2 - 10
placeholderText: qsTr("Password") placeholderText: qsTr("Password")
text: config.userPassword text: config.userPassword
onTextChanged: config.setUserPassword(text) onTextChanged: config.setUserPassword(text)
palette.base: _passwordField.text.length ? "#f0fff0" : "#FBFBFB"
palette.highlight : _passwordField.text.length ? "#dcffdc" : "#FBFBFB"
echoMode: TextInput.Password echoMode: TextInput.Password
passwordMaskDelay: 300 passwordMaskDelay: 300
inputMethodHints: Qt.ImhNoAutoUppercase inputMethodHints: Qt.ImhNoAutoUppercase
background: Rectangle {
color: "#FBFBFB" // Kirigami.Theme.backgroundColor
opacity: 0.9
//border.color: _passwordField.text === "" ? Kirigami.Theme.backgroundColor : ( config.passwordReady ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor)
border.color: _passwordField.text === "" ? "#FBFBFB" : ( config.userPasswordStatusChanged ? "#FBFBFB" : Kirigami.Theme.negativeTextColor)
}
} }
TextField { TextField {
id: _verificationPasswordField id: _verificationPasswordField
width: parent.width / 2 - 10 width: parent.width / 2 - 10
placeholderText: qsTr("Repeat Password") placeholderText: qsTr("Repeat Password")
text: config.userPasswordSecondary text: config.userPasswordSecondary
onTextChanged: config.setUserPasswordSecondary(text)
onTextChanged: _passwordField.text === _verificationPasswordField.text
? ( config.setUserPasswordSecondary(text),
passMessage.visible = false,
validityMessage.visible = true )
: ( passMessage.visible = true,
validityMessage.visible = false )
palette.base: _verificationPasswordField.text.length
? ( _passwordField.text === _verificationPasswordField.text
? "#f0fff0" : "#ffdae0" )
: "#FBFBFB"
palette.highlight : _verificationPasswordField.text.length ? "#dcffdc" : "#FBFBFB"
echoMode: TextInput.Password echoMode: TextInput.Password
passwordMaskDelay: 300 passwordMaskDelay: 300
inputMethodHints: Qt.ImhNoAutoUppercase inputMethodHints: Qt.ImhNoAutoUppercase
background: Rectangle {
color: "#FBFBFB" //Kirigami.Theme.backgroundColor
opacity: 0.9
//border.color: _verificationpasswordField.text === "" ? Kirigami.Theme.backgroundColor : ( config.passwordReady ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor)
border.color: _verificationPasswordField.text === "" ? "#FBFBFB" : ( config.userPasswordSecondaryChanged ? "#FBFBFB" : Kirigami.Theme.negativeTextColor)
}
} }
} }
Label { Label {
width: parent.width width: parent.width
text: qsTr("Enter the same password twice, so that it can be checked for typing errors. A good password will contain a mixture of letters, numbers and punctuation, should be at least eight characters long, and should be changed at regular intervals.") text: qsTr("Enter the same password twice, so that it can be checked for typing errors. A good password will contain a mixture of letters, numbers and punctuation, should be at least eight characters long, and should be changed at regular intervals.")
font.weight: Font.Thin font.weight: Font.Thin
@ -217,27 +237,25 @@ Kirigami.ScrollablePage {
} }
} }
CheckBox { Kirigami.InlineMessage {
id: passMessage
visible: config.permitWeakPasswords Layout.fillWidth: true
text: qsTr("Validate passwords quality") showCloseButton: true
checked: config.requireStrongPasswords visible: false
onCheckedChanged: config.setRequireStrongPasswords(checked) type: Kirigami.MessageType.Error
text: config.userPasswordMessage
} }
Label { Kirigami.InlineMessage {
visible: config.permitWeakPasswords id: validityMessage
width: parent.width Layout.fillWidth: true
text: qsTr("When this box is checked, password-strength checking is done and you will not be able to use a weak password.") showCloseButton: true
font.weight: Font.Thin visible: false
font.pointSize: 8 type: config.userPasswordValidity
color: "#6D6D6D" ? ( config.requireStrongPasswords
} ? Kirigami.MessageType.Error : Kirigami.MessageType.Warning )
: Kirigami.MessageType.Positive
CheckBox { text: config.userPasswordMessage
text: qsTr("Log in automatically without asking for the password")
checked: config.doAutoLogin
onCheckedChanged: config.setAutoLogin(checked)
} }
CheckBox { CheckBox {
@ -258,13 +276,11 @@ Kirigami.ScrollablePage {
} }
Column { Column {
visible: ! root.checked visible: ! root.checked
Layout.fillWidth: true Layout.fillWidth: true
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
Label { Label {
width: parent.width width: parent.width
text: qsTr("Choose a root password to keep your account safe.") text: qsTr("Choose a root password to keep your account safe.")
} }
@ -274,50 +290,46 @@ Kirigami.ScrollablePage {
spacing: 20 spacing: 20
TextField { TextField {
id: _rootPasswordField id: _rootPasswordField
width: parent.width / 2 -10 width: parent.width / 2 -10
placeholderText: qsTr("Root Password") placeholderText: qsTr("Root Password")
text: config.rootPassword text: config.rootPassword
onTextChanged: config.setRootPassword(text) onTextChanged: config.setRootPassword(text)
palette.base: _rootPasswordField.text.length ? "#f0fff0" : "#FBFBFB"
palette.highlight : _rootPasswordField.text.length ? "#dcffdc" : "#FBFBFB"
echoMode: TextInput.Password echoMode: TextInput.Password
passwordMaskDelay: 300 passwordMaskDelay: 300
inputMethodHints: Qt.ImhNoAutoUppercase inputMethodHints: Qt.ImhNoAutoUppercase
background: Rectangle {
color: "#FBFBFB" // Kirigami.Theme.backgroundColor
opacity: 0.9
//border.color: _rootPasswordField.text === "" ? Kirigami.Theme.backgroundColor : ( config.rootPasswordReady ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor)
border.color: _rootPasswordField.text === "" ? "#FBFBFB" : ( config.rootPasswordReady ? "#FBFBFB" : Kirigami.Theme.negativeTextColor)
}
} }
TextField { TextField {
id: _verificationRootPasswordField id: _verificationRootPasswordField
width: parent.width / 2 -10 width: parent.width / 2 -10
placeholderText: qsTr("Repeat Root Password") placeholderText: qsTr("Repeat Root Password")
text: config.rootPasswordSecondary text: config.rootPasswordSecondary
onTextChanged: config.setRootPasswordSecondary(text)
onTextChanged: _rootPasswordField.text === _verificationRootPasswordField.text
? ( config.setRootPasswordSecondary(text),
rootPassMessage.visible = false,
rootValidityMessage.visible = true )
: ( rootPassMessage.visible = true,
rootValidityMessage.visible = false )
palette.base: _verificationRootPasswordField.text.length
? ( _rootPasswordField.text === _verificationRootPasswordField.text
? "#f0fff0" : "#ffdae0") : "#FBFBFB"
palette.highlight : _verificationRootPasswordField.text.length ? "#dcffdc" : "#FBFBFB"
echoMode: TextInput.Password echoMode: TextInput.Password
passwordMaskDelay: 300 passwordMaskDelay: 300
inputMethodHints: Qt.ImhNoAutoUppercase inputMethodHints: Qt.ImhNoAutoUppercase
background: Rectangle {
color: "#FBFBFB" // Kirigami.Theme.backgroundColor
opacity: 0.9
//border.color: _verificationRootPasswordField.text === "" ? Kirigami.Theme.backgroundColor : ( config.rootPasswordReady ? Kirigami.Theme.backgroundColor : Kirigami.Theme.negativeTextColor)
border.color: _verificationRootPasswordField.text === "" ? "#FBFBFB" : ( config.rootPasswordReady ? "#FBFBFB" : Kirigami.Theme.negativeTextColor)
}
} }
} }
Label { Label {
visible: ! root.checked visible: ! root.checked
width: parent.width width: parent.width
text: qsTr("Enter the same password twice, so that it can be checked for typing errors.") text: qsTr("Enter the same password twice, so that it can be checked for typing errors.")
@ -326,5 +338,52 @@ Kirigami.ScrollablePage {
color: "#6D6D6D" color: "#6D6D6D"
} }
} }
Kirigami.InlineMessage {
id: rootPassMessage
Layout.fillWidth: true
showCloseButton: true
visible: false
type: Kirigami.MessageType.Error
text: config.rootPasswordMessage
}
Kirigami.InlineMessage {
id: rootValidityMessage
Layout.fillWidth: true
showCloseButton: true
visible: false
type: config.rootPasswordValidity
? ( config.requireStrongPasswords
? Kirigami.MessageType.Error : Kirigami.MessageType.Warning )
: Kirigami.MessageType.Positive
text: config.rootPasswordMessage
}
CheckBox {
Layout.alignment: Qt.AlignCenter
text: qsTr("Log in automatically without asking for the password")
checked: config.doAutoLogin
onCheckedChanged: config.setAutoLogin(checked)
}
CheckBox {
visible: config.permitWeakPasswords
Layout.alignment: Qt.AlignCenter
text: qsTr("Validate passwords quality")
checked: config.requireStrongPasswords
onCheckedChanged: config.setRequireStrongPasswords(checked),
rootPassMessage.visible = false
}
Label {
visible: config.permitWeakPasswords
width: parent.width
Layout.alignment: Qt.AlignCenter
text: qsTr("When this box is checked, password-strength checking is done and you will not be able to use a weak password.")
font.weight: Font.Thin
font.pointSize: 8
color: "#6D6D6D"
}
} }
} }