From 6388b41e6c69f3fdca172bb6dce1ad770801923b Mon Sep 17 00:00:00 2001 From: demmm Date: Tue, 6 Jul 2021 18:59:58 +0200 Subject: [PATCH 01/87] [partition] adding prettyStatus will be used in summaryq, reading from widgets not an option section probably better suited for Config.cpp/h, since quite a bit of duplicated code from createSummaryWidget --- src/modules/partition/PartitionViewStep.cpp | 90 +++++++++++++++++++++ src/modules/partition/PartitionViewStep.h | 1 + 2 files changed, 91 insertions(+) diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index 015aab2ce..8881d4569 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -109,6 +109,96 @@ PartitionViewStep::prettyName() const return tr( "Partitions" ); } +QString +PartitionViewStep::prettyStatus() const +{ + //return tr( "Create new GPT partition table on /dev/sdb" ); //includes %1" ); .arg ( m_pkgc ); + QString jobsLabel, modeText, diskInfoLabel; + + Config::InstallChoice choice = m_config->installChoice(); + const auto* branding = Calamares::Branding::instance(); + + QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo(); + + cDebug() << "Summary for Partition" << list.length() << choice; + if ( list.length() > 1 ) // There are changes on more than one disk + { +// NOTE: all of this should only happen when Manual partitioning is active. +// Any other choice should result in a list.length() == 1. + switch ( choice ) + { + case Config::Alongside: + modeText = tr( "Install %1 alongside another operating system." ) + .arg( branding->shortVersionedName() ); + break; + case Config::Erase: + modeText + = tr( "Erase disk and install %1." ).arg( branding->shortVersionedName() ); + break; + case Config::Replace: + modeText + = tr( "Replace a partition with %1." ).arg( branding->shortVersionedName() ); + break; + case Config::NoChoice: + case Config::Manual: + modeText = tr( "Manual partitioning." ); + } + } + + for ( const auto& info : list ) + { + if ( list.length() == 1 ) // this is the only disk preview + { + switch ( choice ) + { + case Config::Alongside: + diskInfoLabel = tr( "Install %1 alongside another operating system on disk " + "%2 (%3)." ) + .arg( branding->shortVersionedName() ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + break; + case Config::Erase: + diskInfoLabel = tr( "Erase disk %2 (%3) and install %1." ) + .arg( branding->shortVersionedName() ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + break; + case Config::Replace: + diskInfoLabel = tr( "Replace a partition on disk %2 (%3) with %1." ) + .arg( branding->shortVersionedName() ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + break; + case Config::NoChoice: + case Config::Manual: + diskInfoLabel = tr( "Manual partitioning on disk %1 (%2)." ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + } + } + else // multiple disk previews! + { + diskInfoLabel = tr( "Disk %1 (%2)" ).arg( info.deviceNode ).arg( info.deviceName ) ; + } + } + + QStringList jobsLines; + foreach ( const Calamares::job_ptr& job, jobs() ) + { + if ( !job->prettyDescription().isEmpty() ) + { + jobsLines.append( job->prettyDescription() ); + } + } + if ( !jobsLines.isEmpty() ) + { + jobsLabel = jobsLines.join( "
" ); + } + + return diskInfoLabel + "
" + jobsLabel; +} + QWidget* PartitionViewStep::widget() diff --git a/src/modules/partition/PartitionViewStep.h b/src/modules/partition/PartitionViewStep.h index 9f3da9f3d..ecba73f45 100644 --- a/src/modules/partition/PartitionViewStep.h +++ b/src/modules/partition/PartitionViewStep.h @@ -43,6 +43,7 @@ public: ~PartitionViewStep() override; QString prettyName() const override; + QString prettyStatus() const override; QWidget* createSummaryWidget() const override; QWidget* widget() override; From 6bb7df918dec074ea5193ba0453e8296325a3714 Mon Sep 17 00:00:00 2001 From: demmm Date: Tue, 6 Jul 2021 19:09:20 +0200 Subject: [PATCH 02/87] [summaryq] adding summaryq initial work done by Nitrux/Camilo Higuita in 2020, reflected in license headers C++ adjusted to make it build & work as noted in the inline comments https://github.com/KaOSx/calamares/commit/e80618ef1c90e955d5215ea7803943ce178ec9bb there are quite a few errors in the C++, but it builds, runs and shows the correct output --- src/modules/summaryq/CMakeLists.txt | 22 ++++ src/modules/summaryq/Config.cpp | 114 ++++++++++++++++++ src/modules/summaryq/Config.h | 75 ++++++++++++ src/modules/summaryq/SummaryQmlViewStep.cpp | 75 ++++++++++++ src/modules/summaryq/SummaryQmlViewStep.h | 55 +++++++++ src/modules/summaryq/img/keyboard.svg | 22 ++++ src/modules/summaryq/img/keyboard.svg.license | 2 + src/modules/summaryq/img/lokalize.svg | 39 ++++++ src/modules/summaryq/img/lokalize.svg.license | 2 + src/modules/summaryq/summaryq.qml | 112 +++++++++++++++++ src/modules/summaryq/summaryq.qrc | 7 ++ 11 files changed, 525 insertions(+) create mode 100644 src/modules/summaryq/CMakeLists.txt create mode 100644 src/modules/summaryq/Config.cpp create mode 100644 src/modules/summaryq/Config.h create mode 100644 src/modules/summaryq/SummaryQmlViewStep.cpp create mode 100644 src/modules/summaryq/SummaryQmlViewStep.h create mode 100644 src/modules/summaryq/img/keyboard.svg create mode 100644 src/modules/summaryq/img/keyboard.svg.license create mode 100644 src/modules/summaryq/img/lokalize.svg create mode 100644 src/modules/summaryq/img/lokalize.svg.license create mode 100644 src/modules/summaryq/summaryq.qml create mode 100644 src/modules/summaryq/summaryq.qrc diff --git a/src/modules/summaryq/CMakeLists.txt b/src/modules/summaryq/CMakeLists.txt new file mode 100644 index 000000000..0df854140 --- /dev/null +++ b/src/modules/summaryq/CMakeLists.txt @@ -0,0 +1,22 @@ +if( NOT WITH_QML ) + calamares_skip_module( "summaryq (QML is not supported in this build)" ) + return() +endif() + +set( _summary ${CMAKE_CURRENT_SOURCE_DIR}/../summary ) +include_directories( ${_finished} ) + +include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ) +calamares_add_plugin( summaryq + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + SummaryQmlViewStep.cpp + Config.cpp + UI + RESOURCES + summaryq.qrc + LINK_PRIVATE_LIBRARIES + calamaresui + SHARED_LIB +) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp new file mode 100644 index 000000000..64e43a0ba --- /dev/null +++ b/src/modules/summaryq/Config.cpp @@ -0,0 +1,114 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020, Camilo Higuita + * SPDX-FileCopyrightText: 2021 Anke Boersma + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "Config.h" +#include "SummaryQmlViewStep.h" + +#include "Branding.h" +#include "Settings.h" +#include "ViewManager.h" + +#include "utils/CalamaresUtilsGui.h" +#include "utils/Logger.h" +#include "utils/Retranslator.h" +#include "viewpages/ExecutionViewStep.h" + +SummaryModel::SummaryModel(QObject* parent) : QAbstractListModel(parent) +{} + +QHash SummaryModel::roleNames() const +{ + return { { Qt::DisplayRole, "title" }, { Qt::UserRole, "message" } }; +} + +QVariant SummaryModel::data(const QModelIndex& index, int role) const +{ + if ( !index.isValid() ) + { + return QVariant(); + } + const auto item = m_summary.at( index.row() ); + return role == Qt::DisplayRole ? item->title : item->message; +} + +int SummaryModel::rowCount(const QModelIndex&) const +{ + return m_summary.count(); +} + +void SummaryModel::setSummary(const Calamares::ViewStepList& steps) +{ + m_summary.clear(); + Q_EMIT beginResetModel(); + + for ( Calamares::ViewStep* step : steps ) + { + QString text = step->prettyStatus(); + QWidget* widget = step->createSummaryWidget(); + + if ( text.isEmpty() && !widget ) + continue; + + m_summary << new StepSummary {step->prettyName(), text}; + + } + Q_EMIT endResetModel(); +} + +Config::Config(QObject *parent) : QObject(parent) +, m_thisViewStep(static_cast(parent)) +, m_summary( new SummaryModel(this) ) +{ + m_title = m_thisViewStep->prettyName(); + + if ( Calamares::Settings::instance()->isSetupMode() ) + m_message =( tr( "This is an overview of what will happen once you start " + "the setup procedure." ) ); + else + m_message = ( tr( "This is an overview of what will happen once you start " + "the install procedure." ) ); +} + +void Config::componentComplete() +{ + refresh(); +} + +void Config::refresh() +{ + m_summary->setSummary( stepsForSummary( Calamares::ViewManager::instance()->viewSteps() )); +} + +void Config::init() +{ + refresh(); +} + +Calamares::ViewStepList Config::stepsForSummary( const Calamares::ViewStepList& allSteps ) const +{ + Calamares::ViewStepList steps; + for ( Calamares::ViewStep* step : allSteps ) + { + if ( qobject_cast< Calamares::ExecutionViewStep* >( step ) ) + { + steps.clear(); + continue; + } + + if ( m_thisViewStep == step ) + break; + + steps.append( step ); + } + + return steps; +} + + diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h new file mode 100644 index 000000000..162ff2c5f --- /dev/null +++ b/src/modules/summaryq/Config.h @@ -0,0 +1,75 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019-2020, Adriaan de Groot + * SPDX-FileCopyrightText: 2020, Camilo Higuita + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef SUMMARY_CONFIG_H +#define SUMMARY_CONFIG_H + +#include +#include +#include +#include "viewpages/ViewStep.h" + +class SummaryQmlViewStep; + +struct StepSummary +{ + QString title; + QString message; +}; + +class SummaryModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit SummaryModel(QObject *parent = nullptr); + int rowCount( const QModelIndex& = QModelIndex() ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + + void setSummary(const Calamares::ViewStepList &steps); + +protected: + QHash< int, QByteArray > roleNames() const override; +private: + QVector m_summary; +}; + +class Config : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QString message MEMBER m_message NOTIFY messageChanged CONSTANT) + Q_PROPERTY(QString title MEMBER m_title NOTIFY titleChanged CONSTANT) + Q_PROPERTY(SummaryModel * summaryModel READ summaryModel CONSTANT FINAL) + +public: + explicit Config(QObject *parent = nullptr); + virtual void componentComplete() override; + virtual void classBegin() override {} + + void refresh(); + void init(); + + SummaryModel * summaryModel() const + { + return m_summary; + } + +private: + Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; + const SummaryQmlViewStep* m_thisViewStep; + SummaryModel *m_summary; + + QString m_message; + QString m_title; + +signals: + void messageChanged(); + void titleChanged(); +}; +#endif diff --git a/src/modules/summaryq/SummaryQmlViewStep.cpp b/src/modules/summaryq/SummaryQmlViewStep.cpp new file mode 100644 index 000000000..8f7360212 --- /dev/null +++ b/src/modules/summaryq/SummaryQmlViewStep.cpp @@ -0,0 +1,75 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015, Teo Mrnjavac + * SPDX-FileCopyrightText: 2020, Camilo Higuita + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "SummaryQmlViewStep.h" + +CALAMARES_PLUGIN_FACTORY_DEFINITION( SummaryQmlViewStepFactory, registerPlugin(); ) + +SummaryQmlViewStep::SummaryQmlViewStep( QObject* parent ) + : Calamares::QmlViewStep( parent ) + , m_config( new Config( this ) ) +{ + emit nextStatusChanged( true ); +} + + +SummaryQmlViewStep::~SummaryQmlViewStep() +{ + +} + +QString +SummaryQmlViewStep::prettyName() const +{ + return tr( "Summary" ); +} + + +bool +SummaryQmlViewStep::isNextEnabled() const +{ + return true; +} + + +bool +SummaryQmlViewStep::isBackEnabled() const +{ + return true; +} + + +bool +SummaryQmlViewStep::isAtBeginning() const +{ + return true; +} + + +bool +SummaryQmlViewStep::isAtEnd() const +{ + return true; +} + + +QList< Calamares::job_ptr > +SummaryQmlViewStep::jobs() const +{ + return QList< Calamares::job_ptr >(); +} + + +void +SummaryQmlViewStep::onActivate() +{ + m_config->init(); +} + diff --git a/src/modules/summaryq/SummaryQmlViewStep.h b/src/modules/summaryq/SummaryQmlViewStep.h new file mode 100644 index 000000000..f42ec9f5c --- /dev/null +++ b/src/modules/summaryq/SummaryQmlViewStep.h @@ -0,0 +1,55 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015, Teo Mrnjavac + * SPDX-FileCopyrightText: 2020, Camilo Higuita + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef SUMMARYQMLVIEWSTEP_H +#define SUMMARYQMLVIEWSTEP_H + +#include "Config.h" +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" +#include "DllMacro.h" + +#include + +class SummaryPage; + +class PLUGINDLLEXPORT SummaryQmlViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + explicit SummaryQmlViewStep( QObject* parent = nullptr ); + virtual ~SummaryQmlViewStep() override; + + QString prettyName() const override; + + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + + bool isAtBeginning() const override; + bool isAtEnd() const override; + + QList< Calamares::job_ptr > jobs() const override; + + void onActivate() override; + + QObject * getConfig() override + { + return m_config; + } + +private: + Config *m_config; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( SummaryQmlViewStepFactory ) + +#endif // SUMMARYQMLVIEWSTEP_H diff --git a/src/modules/summaryq/img/keyboard.svg b/src/modules/summaryq/img/keyboard.svg new file mode 100644 index 000000000..6227b788b --- /dev/null +++ b/src/modules/summaryq/img/keyboard.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/summaryq/img/keyboard.svg.license b/src/modules/summaryq/img/keyboard.svg.license new file mode 100644 index 000000000..e59dc6f9c --- /dev/null +++ b/src/modules/summaryq/img/keyboard.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 KDE Visual Design Group +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/modules/summaryq/img/lokalize.svg b/src/modules/summaryq/img/lokalize.svg new file mode 100644 index 000000000..83a7c9dcf --- /dev/null +++ b/src/modules/summaryq/img/lokalize.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/summaryq/img/lokalize.svg.license b/src/modules/summaryq/img/lokalize.svg.license new file mode 100644 index 000000000..e59dc6f9c --- /dev/null +++ b/src/modules/summaryq/img/lokalize.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 KDE Visual Design Group +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/modules/summaryq/summaryq.qml b/src/modules/summaryq/summaryq.qml new file mode 100644 index 000000000..626a42c40 --- /dev/null +++ b/src/modules/summaryq/summaryq.qml @@ -0,0 +1,112 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Anke Boersma + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.15 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 + +Kirigami.ScrollablePage { + width: 860 //parent.width //860 + height: 640 //parent.height //640 + + Kirigami.Theme.backgroundColor: "#EFF0F1" + Kirigami.Theme.textColor: "#1F1F1F" + + header: Kirigami.Heading { + Layout.fillWidth: true + height: 100 + horizontalAlignment: Qt.AlignHCenter + color: Kirigami.Theme.textColor + font.weight: Font.Medium + font.pointSize: 12 + text: config.message + + } + + RowLayout { + width: parent.width + + Component { + id: _delegate + + Rectangle { + id: rect + border.color: "#BDC3C7" + width: parent.width - 80 + implicitHeight: message.implicitHeight + title.implicitHeight + 20 + anchors.horizontalCenter: parent.horizontalCenter + + Item { + width: parent.width - 80 + implicitHeight: message.implicitHeight + title.implicitHeight + 20 + + Kirigami.FormLayout { + + GridLayout { + anchors { + //left: parent.left + top: parent.top + right: parent.right + } + rowSpacing: Kirigami.Units.largeSpacing + columnSpacing: Kirigami.Units.largeSpacing + columns: width > Kirigami.Units.gridUnit * 20 ? 4 : 2 + + Image { + id: image + Layout.maximumHeight: Kirigami.Units.iconSizes.huge + Layout.preferredWidth: height + Layout.alignment: Qt.AlignTop + fillMode: Image.PreserveAspectFit + source: index === 0 ? "img/lokalize.svg" + : ( index === 1 ? "img/keyboard.svg" + : ( index === 2 ? "qrc:/data/images/partition-manual.svg" + : "qrc:/data/images/partition-partition.svg" ) ) + } + ColumnLayout { + + Label { + id: title + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: model.title + font.weight: Font.Medium + font.pointSize: 16 + } + Rectangle { + height: 2 + width: 200 + border.color: "#BDC3C7" + } + Label { + id: message + Layout.fillWidth: true + text: model.message + } + } + } + } + } + } + } + } + + ListView { + anchors.fill: parent + spacing: 20 + model: config.summaryModel + delegate: _delegate + } +} diff --git a/src/modules/summaryq/summaryq.qrc b/src/modules/summaryq/summaryq.qrc new file mode 100644 index 000000000..62bfe7899 --- /dev/null +++ b/src/modules/summaryq/summaryq.qrc @@ -0,0 +1,7 @@ + + + summaryq.qml + img/keyboard.svg + img/lokalize.svg + + From 6ccdf79f7791ecbeea37d160d9177cd73a477d26 Mon Sep 17 00:00:00 2001 From: demmm Date: Tue, 6 Jul 2021 19:30:50 +0200 Subject: [PATCH 03/87] [packagechooserq] adding packagechooserq QML is not using a model as is now, pkgc option is used for setting the default state --- src/modules/packagechooserq/CMakeLists.txt | 69 +++++ .../PackageChooserQmlViewStep.cpp | 86 ++++++ .../PackageChooserQmlViewStep.h | 58 ++++ .../packagechooserq/images/libreoffice.jpg | Bin 0 -> 47916 bytes .../images/libreoffice.jpg.license | 2 + .../packagechooserq/images/no-selection.png | Bin 0 -> 188248 bytes .../images/no-selection.png.license | 2 + src/modules/packagechooserq/images/plasma.png | Bin 0 -> 35256 bytes .../packagechooserq/images/plasma.png.license | 2 + .../packagechooserq/packagechooserq.conf | 48 ++++ .../packagechooserq/packagechooserq.qml | 254 ++++++++++++++++++ .../packagechooserq/packagechooserq.qrc | 8 + 12 files changed, 529 insertions(+) create mode 100644 src/modules/packagechooserq/CMakeLists.txt create mode 100644 src/modules/packagechooserq/PackageChooserQmlViewStep.cpp create mode 100644 src/modules/packagechooserq/PackageChooserQmlViewStep.h create mode 100644 src/modules/packagechooserq/images/libreoffice.jpg create mode 100644 src/modules/packagechooserq/images/libreoffice.jpg.license create mode 100644 src/modules/packagechooserq/images/no-selection.png create mode 100644 src/modules/packagechooserq/images/no-selection.png.license create mode 100644 src/modules/packagechooserq/images/plasma.png create mode 100644 src/modules/packagechooserq/images/plasma.png.license create mode 100644 src/modules/packagechooserq/packagechooserq.conf create mode 100644 src/modules/packagechooserq/packagechooserq.qml create mode 100644 src/modules/packagechooserq/packagechooserq.qrc diff --git a/src/modules/packagechooserq/CMakeLists.txt b/src/modules/packagechooserq/CMakeLists.txt new file mode 100644 index 000000000..0d98c7d1e --- /dev/null +++ b/src/modules/packagechooserq/CMakeLists.txt @@ -0,0 +1,69 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-FileCopyrightText: 2021 Anke Boersma +# SPDX-License-Identifier: BSD-2-Clause +# +if( NOT WITH_QML ) + calamares_skip_module( "packagechooserq (QML is not supported in this build)" ) + return() +endif() + +find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Core ) + +# Add optional libraries here +set( USER_EXTRA_LIB ) + +# include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../packagechooser ) +set( _packagechooser ${CMAKE_CURRENT_SOURCE_DIR}/../packagechooser ) +include_directories( ${_packagechooser} ) + +### OPTIONAL AppData XML support in PackageModel +# +# +# TODO:3.3:WITH->BUILD (this doesn't affect the ABI offered by Calamares) +option( WITH_APPDATA "Support appdata: items in PackageChooser (requires QtXml)" ON ) +if ( WITH_APPDATA ) + find_package(Qt5 COMPONENTS Xml) + if ( Qt5Xml_FOUND ) + add_definitions( -DHAVE_APPDATA ) + list( APPEND _extra_libraries Qt5::Xml ) + list( APPEND _extra_src ItemAppData.cpp ) + endif() +endif() + +### OPTIONAL AppStream support in PackageModel +# +# +option( WITH_APPSTREAM "Support appstream: items in PackageChooser (requires libappstream-qt)" ON ) +if ( WITH_APPSTREAM ) + find_package(AppStreamQt) + set_package_properties( + AppStreamQt PROPERTIES + DESCRIPTION "Support for AppStream (cache) data" + URL "https://github.com/ximion/appstream" + PURPOSE "AppStream provides package data" + TYPE OPTIONAL + ) + if ( AppStreamQt_FOUND ) + add_definitions( -DHAVE_APPSTREAM ) + list( APPEND _extra_libraries AppStreamQt ) + list( APPEND _extra_src ItemAppStream.cpp ) + endif() +endif() + +calamares_add_plugin( packagechooserq + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + PackageChooserQmlViewStep.cpp + ${_packagechooser}/Config.cpp + ${_packagechooser}/PackageModel.cpp + ${_packagechooser}/ItemAppData.cpp + RESOURCES + packagechooserq.qrc + LINK_PRIVATE_LIBRARIES + calamaresui + ${_extra_libraries} + SHARED_LIB +) diff --git a/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp b/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp new file mode 100644 index 000000000..109260ca9 --- /dev/null +++ b/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp @@ -0,0 +1,86 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-FileCopyrightText: 2021 Anke Boersma + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "PackageChooserQmlViewStep.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "locale/TranslatableConfiguration.h" +#include "utils/CalamaresUtilsSystem.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserQmlViewStepFactory, registerPlugin< PackageChooserQmlViewStep >(); ) + +PackageChooserQmlViewStep::PackageChooserQmlViewStep( QObject* parent ) + : Calamares::QmlViewStep( parent ) + , m_config( new Config( this ) ) +{ + emit nextStatusChanged( true ); +} + +QString +PackageChooserQmlViewStep::prettyName() const +{ + return tr( "Packages" ); +} + +QString +PackageChooserQmlViewStep::prettyStatus() const +{ + //QString option = m_pkgc; + //return tr( "Install option: %1" ).arg( option ); + return m_config->prettyStatus(); +} + +bool +PackageChooserQmlViewStep::isNextEnabled() const +{ + return true; +} + +bool +PackageChooserQmlViewStep::isBackEnabled() const +{ + return true; +} + +bool +PackageChooserQmlViewStep::isAtBeginning() const +{ + return true; +} + +bool +PackageChooserQmlViewStep::isAtEnd() const +{ + return true; +} + +Calamares::JobList +PackageChooserQmlViewStep::jobs() const +{ + Calamares::JobList l; + return l; +} + +void +PackageChooserQmlViewStep::onLeave() +{ + m_config->fillGSSecondaryConfiguration(); +} + +void +PackageChooserQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + m_config->setDefaultId( moduleInstanceKey() ); + m_config->setConfigurationMap( configurationMap ); + Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last +} diff --git a/src/modules/packagechooserq/PackageChooserQmlViewStep.h b/src/modules/packagechooserq/PackageChooserQmlViewStep.h new file mode 100644 index 000000000..1ac2451c2 --- /dev/null +++ b/src/modules/packagechooserq/PackageChooserQmlViewStep.h @@ -0,0 +1,58 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-FileCopyrightText: 2021 Anke Boersma + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef PACKAGECHOOSERQMLVIEWSTEP_H +#define PACKAGECHOOSERQMLVIEWSTEP_H + +// Config from packagechooser module +#include "Config.h" + +#include "DllMacro.h" +#include "locale/TranslatableConfiguration.h" +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" + +#include + +class Config; +class PackageChooserPage; + +class PLUGINDLLEXPORT PackageChooserQmlViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + explicit PackageChooserQmlViewStep( QObject* parent = nullptr ); + + QString prettyName() const override; + QString prettyStatus() const override; + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + + bool isAtBeginning() const override; + bool isAtEnd() const override; + + //void onActivate() override; + void onLeave() override; + + Calamares::JobList jobs() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + + QObject* getConfig() override { return m_config; } + +private: + Config* m_config; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserQmlViewStepFactory ) + +#endif // PACKAGECHOOSERQMLVIEWSTEP_H diff --git a/src/modules/packagechooserq/images/libreoffice.jpg b/src/modules/packagechooserq/images/libreoffice.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e216cc77a3e2df33f37679daea382e1a827f640b GIT binary patch literal 47916 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 literal 0 HcmV?d00001 diff --git a/src/modules/packagechooserq/images/libreoffice.jpg.license b/src/modules/packagechooserq/images/libreoffice.jpg.license new file mode 100644 index 000000000..5f43e650d --- /dev/null +++ b/src/modules/packagechooserq/images/libreoffice.jpg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2020 demmm +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/modules/packagechooserq/images/no-selection.png b/src/modules/packagechooserq/images/no-selection.png new file mode 100644 index 0000000000000000000000000000000000000000..b32d2170215e9337a31614c26a6b60b5f11426f9 GIT binary patch literal 188248 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 z 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

    Kpsl1eXLr6 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 Vl||K8re**D002ovPDHLkV1n&~wVMC{ literal 0 HcmV?d00001 diff --git a/src/modules/packagechooserq/images/no-selection.png.license b/src/modules/packagechooserq/images/no-selection.png.license new file mode 100644 index 000000000..5f43e650d --- /dev/null +++ b/src/modules/packagechooserq/images/no-selection.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2020 demmm +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/modules/packagechooserq/images/plasma.png b/src/modules/packagechooserq/images/plasma.png new file mode 100644 index 0000000000000000000000000000000000000000..e9e35a5285e7a6b421ac0693181c957794c2b587 GIT binary patch literal 35256 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 V1#h=Y2QU&7C%BD2KF(|Pe*rwi%gO)% literal 0 HcmV?d00001 diff --git a/src/modules/packagechooserq/images/plasma.png.license b/src/modules/packagechooserq/images/plasma.png.license new file mode 100644 index 000000000..16c11f071 --- /dev/null +++ b/src/modules/packagechooserq/images/plasma.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2021 pngegg +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/modules/packagechooserq/packagechooserq.conf b/src/modules/packagechooserq/packagechooserq.conf new file mode 100644 index 000000000..fc45e4fa9 --- /dev/null +++ b/src/modules/packagechooserq/packagechooserq.conf @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the low-density software chooser +--- +# Software selection mode, to set whether the software packages +# can be chosen singly, or multiply. +# +# Possible modes are "optional", "required" (for zero-or-one or exactly-one) +# or "optionalmultiple", "requiredmultiple" (for zero-or-more +# or one-or-more). +mode: required + +# Software installation method: +# +# - "legacy" or "custom" or "contextualprocess" +# When set to "legacy", writes a GlobalStorage value for the choice that +# has been made. The key is *packagechooser_*. Normally, the module's +# instance name is used; see the *instances* section of `settings.conf`. +# If there is just one packagechooser module, and no special instance is set, +# resulting GS key is probably *packagechooser@packagechooser*. +# You can set *id* to change that, but it is not recommended. +# +# The GS value is a comma-separated list of the IDs of the selected +# packages, or an empty string if none is selected. +# +# With "legacy" installation, you should have a contextualprocess or similar +# module somewhere in the `exec` phase to process the GlobalStorage key +# and actually **do** something for the packages. +# +# - "packages" +# When set to "packages", writes GlobalStorage values suitable for +# consumption by the *packages* module (which should appear later +# in the `exec` section. These package settings will then be handed +# off to whatever package manager is configured there. +# The *id* key is not used. +# +# There is no need to put this module in the `exec` section. There +# are no jobs that this module provides. You should put **other** +# modules, either *contextualprocess* or *packages* or some custom +# module, in the `exec` section to do the actual work. +method: legacy +# The *id* key is used only in "legacy" mode +# id: "" + +# The *pkgc* is used for setting the default selection in the QML view +pkgc: libreoffice + diff --git a/src/modules/packagechooserq/packagechooserq.qml b/src/modules/packagechooserq/packagechooserq.qml new file mode 100644 index 000000000..cf7454fe9 --- /dev/null +++ b/src/modules/packagechooserq/packagechooserq.qml @@ -0,0 +1,254 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Anke Boersma + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.3 + +Item { + width: parent.width + height: parent.height + + Rectangle { + anchors.fill: parent + color: "#f2f2f2" + + ButtonGroup { + id: switchGroup + } + + Column { + id: column + anchors.centerIn: parent + spacing: 5 + + Rectangle { + //id: rectangle + width: 700 + height: 150 + color: "#ffffff" + radius: 10 + border.width: 0 + Text { + width: 450 + height: 104 + anchors.centerIn: parent + text: qsTr("LibreOffice is a powerful and free office suite, used by millions of people around the world. It includes several applications that make it the most versatile Free and Open Source office suite on the market.
    + Default option.") + font.pointSize: 10 + anchors.verticalCenterOffset: -10 + anchors.horizontalCenterOffset: 100 + wrapMode: Text.WordWrap + } + + Switch { + id: element2 + x: 500 + y: 110 + width: 187 + height: 14 + text: qsTr("LibreOffice") + checked: true + hoverEnabled: true + ButtonGroup.group: switchGroup + + indicator: Rectangle { + implicitWidth: 40 + implicitHeight: 14 + radius: 10 + color: element2.checked ? "#3498db" : "#B9B9B9" + border.color: element2.checked ? "#3498db" : "#cccccc" + + Rectangle { + x: element2.checked ? parent.width - width : 0 + y: (parent.height - height) / 2 + width: 20 + height: 20 + radius: 10 + color: element2.down ? "#cccccc" : "#ffffff" + border.color: element2.checked ? (element1.down ? "#3498db" : "#3498db") : "#999999" + } + } + + onCheckedChanged: { + if ( ! checked ) { + print("L not used") + } + else { + config.pkgc = "libreoffice" + print( config.pkgc ) + } + } + } + + Image { + id: image2 + x: 8 + y: 25 + height: 100 + fillMode: Image.PreserveAspectFit + source: "images/libreoffice.jpg" + } + } + + Rectangle { + width: 700 + height: 150 + radius: 10 + border.width: 0 + Text { + width: 450 + height: 104 + anchors.centerIn: parent + text: qsTr("If you don't want to install an office suite, just select No Office Suite. You can always add one (or more) later on your installed system as the need arrives.") + font.pointSize: 10 + anchors.verticalCenterOffset: -10 + anchors.horizontalCenterOffset: 100 + wrapMode: Text.WordWrap + } + + Switch { + id: element1 + x: 500 + y: 110 + width: 187 + height: 14 + text: qsTr("No Office Suite") + checked: false + hoverEnabled: true + ButtonGroup.group: switchGroup + + indicator: Rectangle { + implicitWidth: 40 + implicitHeight: 14 + radius: 10 + color: element1.checked ? "#3498db" : "#B9B9B9" + border.color: element1.checked ? "#3498db" : "#cccccc" + + Rectangle { + x: element1.checked ? parent.width - width : 0 + y: (parent.height - height) / 2 + width: 20 + height: 20 + radius: 10 + color: element1.down ? "#cccccc" : "#ffffff" + border.color: element1.checked ? (element1.down ? "#3498db" : "#3498db") : "#999999" + } + } + + onCheckedChanged: { + if ( ! checked ) { + print("not used") + //console.log("removed") + } + else { + print("No Office Suite") + config.pkgc = "no_office_suite" + } + } + } + + Image { + id: image + x: 8 + y: 25 + height: 100 + fillMode: Image.PreserveAspectFit + source: "images/no-selection.png" + } + + } + + Rectangle { + width: 700 + height: 150 + color: "#ffffff" + radius: 10 + border.width: 0 + Text { + width: 450 + height: 104 + anchors.centerIn: parent + text: qsTr("Create a minimal Desktop install, remove all extra applications and decide later on what you would like to add to your system. Examples of what won't be on such an install, there will be no Office Suite, no media players, no image viewer or print support. It will be just a desktop, file browser, package manager, text editor and simple web-browser.") + font.pointSize: 10 + anchors.verticalCenterOffset: -10 + anchors.horizontalCenterOffset: 100 + wrapMode: Text.WordWrap + } + + Switch { + id: element3 + x: 500 + y: 110 + width: 187 + height: 14 + text: qsTr("Minimal Install") + checked: false + hoverEnabled: true + ButtonGroup.group: switchGroup + + indicator: Rectangle { + implicitWidth: 40 + implicitHeight: 14 + radius: 10 + color: element3.checked ? "#3498db" : "#B9B9B9" + border.color: element3.checked ? "#3498db" : "#cccccc" + + Rectangle { + x: element3.checked ? parent.width - width : 0 + y: (parent.height - height) / 2 + width: 20 + height: 20 + radius: 10 + color: element3.down ? "#cccccc" : "#ffffff" + border.color: element3.checked ? (element3.down ? "#3498db" : "#3498db") : "#999999" + } + } + + onCheckedChanged: { + if ( ! checked ) { + print("M not used") + } + else { + print("minimal") + config.pkgc = "minimal_install" + } + } + } + + Image { + id: image3 + x: 8 + y: 25 + height: 100 + fillMode: Image.PreserveAspectFit + source: "images/plasma.png" + } + } + + Rectangle { + width: 700 + height: 25 + color: "#f2f2f2" + border.width: 0 + Text { + height: 25 + anchors.centerIn: parent + text: qsTr("Please select an option for your install, or use the default: LibreOffice included.") + font.pointSize: 10 + wrapMode: Text.WordWrap + } + } + } + } + +} diff --git a/src/modules/packagechooserq/packagechooserq.qrc b/src/modules/packagechooserq/packagechooserq.qrc new file mode 100644 index 000000000..1b892dce1 --- /dev/null +++ b/src/modules/packagechooserq/packagechooserq.qrc @@ -0,0 +1,8 @@ + + + packagechooserq.qml + images/libreoffice.jpg + images/no-selection.png + images/plasma.png + + From 373b94b96880d70774cbdaf50fca75df762c6df5 Mon Sep 17 00:00:00 2001 From: demmm Date: Tue, 6 Jul 2021 19:37:28 +0200 Subject: [PATCH 04/87] [packagechooser] Config files adjusted for new QML modules function added to store selections from packagechooserq line 103 in Config.cpp needs adjusting to restore working regular widget based packagechooser prettyStatus added, made visible in packagechooserq only, ViewStep not altered in packagechooser for this yet --- src/modules/packagechooser/Config.cpp | 17 ++++++++++++++++- src/modules/packagechooser/Config.h | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/modules/packagechooser/Config.cpp b/src/modules/packagechooser/Config.cpp index 106ae4538..f66f16824 100644 --- a/src/modules/packagechooser/Config.cpp +++ b/src/modules/packagechooser/Config.cpp @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-FileCopyrightText: 2021 Anke Boersma * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -99,7 +100,8 @@ Config::updateGlobalStorage( const QStringList& selected ) const { if ( m_method == PackageChooserMethod::Legacy ) { - QString value = selected.join( ',' ); + //QString value = selected.join( ',' ); + QString value = ( m_pkgc ); Calamares::JobQueue::instance()->globalStorage()->insert( m_id, value ); cDebug() << m_id<< "selected" << value; } @@ -116,6 +118,18 @@ Config::updateGlobalStorage( const QStringList& selected ) const } } +void +Config::setPkgc( const QString& pkgc ) +{ + m_pkgc = pkgc; + emit pkgcChanged( m_pkgc ); +} + +QString +Config::prettyStatus() const +{ + return tr( "Install option: %1" ).arg( m_pkgc ); +} static void fillModel( PackageListModel* model, const QVariantList& items ) @@ -183,6 +197,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) PackageChooserMode::Required ); m_method = PackageChooserMethodNames().find( CalamaresUtils::getString( configurationMap, "method" ), PackageChooserMethod::Legacy ); + m_pkgc = CalamaresUtils::getString( configurationMap, "pkgc" ); if ( m_method == PackageChooserMethod::Legacy ) { diff --git a/src/modules/packagechooser/Config.h b/src/modules/packagechooser/Config.h index 5959e3ea4..b343a8cb2 100644 --- a/src/modules/packagechooser/Config.h +++ b/src/modules/packagechooser/Config.h @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-FileCopyrightText: 2021 Anke Boersma * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -39,6 +40,9 @@ class Config : public Calamares::ModuleSystem::Config { Q_OBJECT + Q_PROPERTY( QString pkgc READ pkgc WRITE setPkgc NOTIFY pkgcChanged ) + Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL ) + public: Config( QObject* parent = nullptr ); ~Config() override; @@ -74,6 +78,15 @@ public: /// As updateGlobalStorage() with an empty selection list void fillGSSecondaryConfiguration() const { updateGlobalStorage( QStringList() ); } + QString pkgc() const { return m_pkgc; } + void setPkgc( const QString& pkgc ); + + QString prettyStatus() const; + +signals: + void pkgcChanged( QString pkgc ); + void prettyStatusChanged(); + private: PackageListModel* m_model = nullptr; QModelIndex m_defaultModelIndex; @@ -86,6 +99,8 @@ private: QString m_id; /// Value to use for id if none is set in the config file Calamares::ModuleSystem::InstanceKey m_defaultId; + /// QML selection + QString m_pkgc; }; From 7094c7bf39a1f6d625fb3db8344da5e60002807a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 12 Jul 2021 14:56:04 +0200 Subject: [PATCH 05/87] [partition] Minor stylistic bits --- src/modules/partition/PartitionViewStep.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index 8881d4569..5b225811e 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -112,7 +112,6 @@ PartitionViewStep::prettyName() const QString PartitionViewStep::prettyStatus() const { - //return tr( "Create new GPT partition table on /dev/sdb" ); //includes %1" ); .arg ( m_pkgc ); QString jobsLabel, modeText, diskInfoLabel; Config::InstallChoice choice = m_config->installChoice(); @@ -123,8 +122,8 @@ PartitionViewStep::prettyStatus() const cDebug() << "Summary for Partition" << list.length() << choice; if ( list.length() > 1 ) // There are changes on more than one disk { -// NOTE: all of this should only happen when Manual partitioning is active. -// Any other choice should result in a list.length() == 1. + // NOTE: all of this should only happen when Manual partitioning is active. + // Any other choice should result in a list.length() == 1. switch ( choice ) { case Config::Alongside: @@ -199,14 +198,6 @@ PartitionViewStep::prettyStatus() const return diskInfoLabel + "
    " + jobsLabel; } - -QWidget* -PartitionViewStep::widget() -{ - return m_widget; -} - - QWidget* PartitionViewStep::createSummaryWidget() const { @@ -355,6 +346,11 @@ PartitionViewStep::createSummaryWidget() const return widget; } +QWidget* +PartitionViewStep::widget() +{ + return m_widget; +} void PartitionViewStep::next() From 535f81ba8a8086c2ef4b9cf91cdda5c265459276 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 12 Jul 2021 15:02:24 +0200 Subject: [PATCH 06/87] [partition] Factor out gather job-descriptions Since prettyStatus() and the summaryWidget share a lot of strings and code, start factoring that out. --- src/modules/partition/PartitionViewStep.cpp | 31 ++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index 5b225811e..d4a032673 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -109,6 +109,19 @@ PartitionViewStep::prettyName() const return tr( "Partitions" ); } +static QStringList jobDescriptions( const Calamares::JobList& jobs ) +{ + QStringList jobsLines; + for( const Calamares::job_ptr& job : qAsConst( jobs ) ) + { + if ( !job->prettyDescription().isEmpty() ) + { + jobsLines.append( job->prettyDescription() ); + } + } + return jobsLines; +} + QString PartitionViewStep::prettyStatus() const { @@ -182,14 +195,7 @@ PartitionViewStep::prettyStatus() const } } - QStringList jobsLines; - foreach ( const Calamares::job_ptr& job, jobs() ) - { - if ( !job->prettyDescription().isEmpty() ) - { - jobsLines.append( job->prettyDescription() ); - } - } + const QStringList jobsLines = jobDescriptions( jobs() ); if ( !jobsLines.isEmpty() ) { jobsLabel = jobsLines.join( "
    " ); @@ -324,14 +330,7 @@ PartitionViewStep::createSummaryWidget() const field->addWidget( previewLabels ); formLayout->addRow( tr( "After:" ), field ); } - QStringList jobsLines; - foreach ( const Calamares::job_ptr& job, jobs() ) - { - if ( !job->prettyDescription().isEmpty() ) - { - jobsLines.append( job->prettyDescription() ); - } - } + const QStringList jobsLines = jobDescriptions( jobs() ); if ( !jobsLines.isEmpty() ) { QLabel* jobsLabel = new QLabel( widget ); From 400a5751f916302762d1c3ed16ec9fd8c625b10c Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 12 Jul 2021 15:17:34 +0200 Subject: [PATCH 07/87] [partition] Factor out description of action in status and widget --- src/modules/partition/PartitionViewStep.cpp | 64 +++++++++------------ 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index d4a032673..7576c7a05 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -122,6 +122,30 @@ static QStringList jobDescriptions( const Calamares::JobList& jobs ) return jobsLines; } +static QString modeDescription( Config::InstallChoice choice ) +{ + const auto* branding = Calamares::Branding::instance(); + static const char context[] = "PartitionViewStep"; + + switch ( choice ) + { + case Config::InstallChoice::Alongside: + return QCoreApplication::translate( context, "Install %1 alongside another operating system." ) + .arg( branding->shortVersionedName() ); + break; + case Config::InstallChoice::Erase: + return QCoreApplication::translate( context, "Erase disk and install %1." ).arg( branding->shortVersionedName() ); + break; + case Config::InstallChoice::Replace: + return QCoreApplication::translate( context, "Replace a partition with %1." ).arg( branding->shortVersionedName() ); + break; + case Config::InstallChoice::NoChoice: + case Config::InstallChoice::Manual: + return QCoreApplication::translate( context, "Manual partitioning." ); + } + return QString(); +} + QString PartitionViewStep::prettyStatus() const { @@ -135,26 +159,7 @@ PartitionViewStep::prettyStatus() const cDebug() << "Summary for Partition" << list.length() << choice; if ( list.length() > 1 ) // There are changes on more than one disk { - // NOTE: all of this should only happen when Manual partitioning is active. - // Any other choice should result in a list.length() == 1. - switch ( choice ) - { - case Config::Alongside: - modeText = tr( "Install %1 alongside another operating system." ) - .arg( branding->shortVersionedName() ); - break; - case Config::Erase: - modeText - = tr( "Erase disk and install %1." ).arg( branding->shortVersionedName() ); - break; - case Config::Replace: - modeText - = tr( "Replace a partition with %1." ).arg( branding->shortVersionedName() ); - break; - case Config::NoChoice: - case Config::Manual: - modeText = tr( "Manual partitioning." ); - } + modeText = modeDescription(choice); } for ( const auto& info : list ) @@ -227,24 +232,7 @@ PartitionViewStep::createSummaryWidget() const // Any other choice should result in a list.length() == 1. QLabel* modeLabel = new QLabel; formLayout->addRow( modeLabel ); - QString modeText; - switch ( choice ) - { - case Config::InstallChoice::Alongside: - modeText = tr( "Install %1 alongside another operating system." ) - .arg( branding->shortVersionedName() ); - break; - case Config::InstallChoice::Erase: - modeText = tr( "Erase disk and install %1." ).arg( branding->shortVersionedName() ); - break; - case Config::InstallChoice::Replace: - modeText = tr( "Replace a partition with %1." ).arg( branding->shortVersionedName() ); - break; - case Config::InstallChoice::NoChoice: - case Config::InstallChoice::Manual: - modeText = tr( "Manual partitioning." ); - } - modeLabel->setText( modeText ); + modeLabel->setText( modeDescription( choice ) ); } for ( const auto& info : list ) { From e8b17b9878f8ead237aa76e92be31fd99a598fd4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 12 Jul 2021 15:39:12 +0200 Subject: [PATCH 08/87] [partition] Factor out the descriptions per-disk --- src/modules/partition/PartitionViewStep.cpp | 142 +++++++++----------- 1 file changed, 65 insertions(+), 77 deletions(-) diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index 7576c7a05..69ea4cb2e 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -109,6 +109,12 @@ PartitionViewStep::prettyName() const return tr( "Partitions" ); } +/** @brief Gather the pretty descriptions of all the partitioning jobs + * + * Returns a QStringList of each job's pretty description, including + * empty strings and duplicates. The list is in-order of how the + * jobs will be run. + */ static QStringList jobDescriptions( const Calamares::JobList& jobs ) { QStringList jobsLines; @@ -122,6 +128,10 @@ static QStringList jobDescriptions( const Calamares::JobList& jobs ) return jobsLines; } +/** @brief A top-level description of what @p choice does + * + * Returns a (branded) string describing what @p choice will do. + */ static QString modeDescription( Config::InstallChoice choice ) { const auto* branding = Calamares::Branding::instance(); @@ -146,15 +156,62 @@ static QString modeDescription( Config::InstallChoice choice ) return QString(); } +/** @brief A top-level description of what @p choice does to disk @p info + * + * Returns a (branded, and device-specific) string describing what + * will be done to device @p info when @p choice is made. The @p listLength + * is used to provide context; when more than one disk is in use, the description + * works differently. + */ +static QString diskDescription( int listLength, const PartitionCoreModule::SummaryInfo& info, Config::InstallChoice choice ) +{ + const auto* branding = Calamares::Branding::instance(); + static const char context[] = "PartitionViewStep"; + + if ( listLength == 1 ) // this is the only disk preview + { + switch ( choice ) + { + case Config::Alongside: + return QCoreApplication::translate( context, "Install %1 alongside another operating system on disk " + "%2 (%3)." ) + .arg( branding->shortVersionedName() ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + break; + case Config::Erase: + return QCoreApplication::translate( context, "Erase disk %2 (%3) and install %1." ) + .arg( branding->shortVersionedName() ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + break; + case Config::Replace: + return QCoreApplication::translate( context, "Replace a partition on disk %2 (%3) with %1." ) + .arg( branding->shortVersionedName() ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + break; + case Config::NoChoice: + case Config::Manual: + return QCoreApplication::translate( context, "Manual partitioning on disk %1 (%2)." ) + .arg( info.deviceNode ) + .arg( info.deviceName ); + } + return QString(); + } + else // multiple disk previews! + { + return QCoreApplication::translate( context, "Disk %1 (%2)" ).arg( info.deviceNode ).arg( info.deviceName ) ; + } +} + QString PartitionViewStep::prettyStatus() const { QString jobsLabel, modeText, diskInfoLabel; - Config::InstallChoice choice = m_config->installChoice(); - const auto* branding = Calamares::Branding::instance(); - - QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo(); + const Config::InstallChoice choice = m_config->installChoice(); + const QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo(); cDebug() << "Summary for Partition" << list.length() << choice; if ( list.length() > 1 ) // There are changes on more than one disk @@ -164,40 +221,8 @@ PartitionViewStep::prettyStatus() const for ( const auto& info : list ) { - if ( list.length() == 1 ) // this is the only disk preview - { - switch ( choice ) - { - case Config::Alongside: - diskInfoLabel = tr( "Install %1 alongside another operating system on disk " - "%2 (%3)." ) - .arg( branding->shortVersionedName() ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - break; - case Config::Erase: - diskInfoLabel = tr( "Erase disk %2 (%3) and install %1." ) - .arg( branding->shortVersionedName() ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - break; - case Config::Replace: - diskInfoLabel = tr( "Replace a partition on disk %2 (%3) with %1." ) - .arg( branding->shortVersionedName() ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - break; - case Config::NoChoice: - case Config::Manual: - diskInfoLabel = tr( "Manual partitioning on disk %1 (%2)." ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - } - } - else // multiple disk previews! - { - diskInfoLabel = tr( "Disk %1 (%2)" ).arg( info.deviceNode ).arg( info.deviceName ) ; - } + // TODO: this overwrites each iteration + diskInfoLabel = diskDescription( list.length(), info, choice ); } const QStringList jobsLines = jobDescriptions( jobs() ); @@ -224,8 +249,7 @@ PartitionViewStep::createSummaryWidget() const formLayout->setContentsMargins( MARGIN, 0, MARGIN, MARGIN ); mainLayout->addLayout( formLayout ); - const auto* branding = Calamares::Branding::instance(); - QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo(); + const QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo(); if ( list.length() > 1 ) // There are changes on more than one disk { //NOTE: all of this should only happen when Manual partitioning is active. @@ -237,43 +261,7 @@ PartitionViewStep::createSummaryWidget() const for ( const auto& info : list ) { QLabel* diskInfoLabel = new QLabel; - if ( list.length() == 1 ) // this is the only disk preview - { - QString modeText; - switch ( choice ) - { - case Config::InstallChoice::Alongside: - modeText = tr( "Install %1 alongside another operating system on disk " - "%2 (%3)." ) - .arg( branding->shortVersionedName() ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - break; - case Config::InstallChoice::Erase: - modeText = tr( "Erase disk %2 (%3) and install %1." ) - .arg( branding->shortVersionedName() ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - break; - case Config::InstallChoice::Replace: - modeText = tr( "Replace a partition on disk %2 (%3) with %1." ) - .arg( branding->shortVersionedName() ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - break; - case Config::InstallChoice::NoChoice: - case Config::InstallChoice::Manual: - modeText = tr( "Manual partitioning on disk %1 (%2)." ) - .arg( info.deviceNode ) - .arg( info.deviceName ); - } - diskInfoLabel->setText( modeText ); - } - else // multiple disk previews! - { - diskInfoLabel->setText( - tr( "Disk %1 (%2)" ).arg( info.deviceNode ).arg( info.deviceName ) ); - } + diskInfoLabel->setText( diskDescription( list.length(), info, choice ) ); formLayout->addRow( diskInfoLabel ); PartitionBarsView* preview; From b43759c6a5eaa93e2711988e21a57a2091574a69 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 12 Jul 2021 15:42:54 +0200 Subject: [PATCH 09/87] [partition] Apply coding style --- src/modules/partition/Config.cpp | 14 +-- src/modules/partition/Config.h | 5 +- src/modules/partition/PartitionViewStep.cpp | 61 +++++++----- src/modules/partition/core/PartUtils.cpp | 32 +++--- .../partition/core/PartitionLayout.cpp | 98 ++++++++++--------- src/modules/partition/gui/ChoicePage.cpp | 7 +- 6 files changed, 117 insertions(+), 100 deletions(-) diff --git a/src/modules/partition/Config.cpp b/src/modules/partition/Config.cpp index 82c0ad846..508231a75 100644 --- a/src/modules/partition/Config.cpp +++ b/src/modules/partition/Config.cpp @@ -227,7 +227,8 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu QString firmwareType( PartUtils::isEfiSystem() ? QStringLiteral( "efi" ) : QStringLiteral( "bios" ) ); gs->insert( "firmwareType", firmwareType ); - gs->insert( "efiSystemPartition", CalamaresUtils::getString( configurationMap, "efiSystemPartition", QStringLiteral( "/boot/efi" ) ) ); + gs->insert( "efiSystemPartition", + CalamaresUtils::getString( configurationMap, "efiSystemPartition", QStringLiteral( "/boot/efi" ) ) ); // Read and parse key efiSystemPartitionSize if ( configurationMap.contains( "efiSystemPartitionSize" ) ) @@ -243,7 +244,7 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu } void -Config::fillConfigurationFSTypes(const QVariantMap& configurationMap) +Config::fillConfigurationFSTypes( const QVariantMap& configurationMap ) { Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); @@ -256,15 +257,16 @@ Config::fillConfigurationFSTypes(const QVariantMap& configurationMap) if ( fsName.isEmpty() ) { cWarning() << "Partition-module setting *defaultFileSystemType* is missing, will use ext4"; - fsRealName = PartUtils::canonicalFilesystemName( QStringLiteral("ext4"), &fsType ); + 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 ); + cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << fsName + << ") using ext4 instead"; + fsRealName = PartUtils::canonicalFilesystemName( QStringLiteral( "ext4" ), &fsType ); } else if ( fsRealName != fsName ) { @@ -326,7 +328,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" ); gs->insert( "requiredPartitionTableType", m_requiredPartitionTableType ); - fillGSConfigurationEFI(gs, configurationMap); + fillGSConfigurationEFI( gs, configurationMap ); fillConfigurationFSTypes( configurationMap ); } diff --git a/src/modules/partition/Config.h b/src/modules/partition/Config.h index 3365403fa..5f7e46821 100644 --- a/src/modules/partition/Config.h +++ b/src/modules/partition/Config.h @@ -27,7 +27,8 @@ class Config : public QObject 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( + QString eraseModeFilesystem READ eraseFsType WRITE setEraseFsTypeChoice NOTIFY eraseModeFilesystemChanged ) Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL ) @@ -134,7 +135,7 @@ public Q_SLOTS: void setInstallChoice( InstallChoice ); void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice void setSwapChoice( SwapChoice ); - void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem + void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem Q_SIGNALS: void installChoiceChanged( InstallChoice ); diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index 69ea4cb2e..a3d6e2db5 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -115,10 +115,11 @@ PartitionViewStep::prettyName() const * empty strings and duplicates. The list is in-order of how the * jobs will be run. */ -static QStringList jobDescriptions( const Calamares::JobList& jobs ) +static QStringList +jobDescriptions( const Calamares::JobList& jobs ) { QStringList jobsLines; - for( const Calamares::job_ptr& job : qAsConst( jobs ) ) + for ( const Calamares::job_ptr& job : qAsConst( jobs ) ) { if ( !job->prettyDescription().isEmpty() ) { @@ -132,7 +133,8 @@ static QStringList jobDescriptions( const Calamares::JobList& jobs ) * * Returns a (branded) string describing what @p choice will do. */ -static QString modeDescription( Config::InstallChoice choice ) +static QString +modeDescription( Config::InstallChoice choice ) { const auto* branding = Calamares::Branding::instance(); static const char context[] = "PartitionViewStep"; @@ -141,13 +143,15 @@ static QString modeDescription( Config::InstallChoice choice ) { case Config::InstallChoice::Alongside: return QCoreApplication::translate( context, "Install %1 alongside another operating system." ) - .arg( branding->shortVersionedName() ); + .arg( branding->shortVersionedName() ); break; case Config::InstallChoice::Erase: - return QCoreApplication::translate( context, "Erase disk and install %1." ).arg( branding->shortVersionedName() ); + return QCoreApplication::translate( context, "Erase disk and install %1." ) + .arg( branding->shortVersionedName() ); break; case Config::InstallChoice::Replace: - return QCoreApplication::translate( context, "Replace a partition with %1." ).arg( branding->shortVersionedName() ); + return QCoreApplication::translate( context, "Replace a partition with %1." ) + .arg( branding->shortVersionedName() ); break; case Config::InstallChoice::NoChoice: case Config::InstallChoice::Manual: @@ -163,7 +167,8 @@ static QString modeDescription( Config::InstallChoice choice ) * is used to provide context; when more than one disk is in use, the description * works differently. */ -static QString diskDescription( int listLength, const PartitionCoreModule::SummaryInfo& info, Config::InstallChoice choice ) +static QString +diskDescription( int listLength, const PartitionCoreModule::SummaryInfo& info, Config::InstallChoice choice ) { const auto* branding = Calamares::Branding::instance(); static const char context[] = "PartitionViewStep"; @@ -172,28 +177,33 @@ static QString diskDescription( int listLength, const PartitionCoreModule::Summa { switch ( choice ) { - case Config::Alongside: - return QCoreApplication::translate( context, "Install %1 alongside another operating system on disk " - "%2 (%3)." ) + case Config::Alongside: + return QCoreApplication::translate( + context, + "Install %1 alongside another operating system on disk " + "%2 (%3)." ) .arg( branding->shortVersionedName() ) .arg( info.deviceNode ) .arg( info.deviceName ); - break; - case Config::Erase: - return QCoreApplication::translate( context, "Erase disk %2 (%3) and install %1." ) + break; + case Config::Erase: + return QCoreApplication::translate( context, + "Erase disk %2 (%3) and install %1." ) .arg( branding->shortVersionedName() ) .arg( info.deviceNode ) .arg( info.deviceName ); - break; - case Config::Replace: - return QCoreApplication::translate( context, "Replace a partition on disk %2 (%3) with %1." ) + break; + case Config::Replace: + return QCoreApplication::translate( + context, "Replace a partition on disk %2 (%3) with %1." ) .arg( branding->shortVersionedName() ) .arg( info.deviceNode ) .arg( info.deviceName ); - break; - case Config::NoChoice: - case Config::Manual: - return QCoreApplication::translate( context, "Manual partitioning on disk %1 (%2)." ) + break; + case Config::NoChoice: + case Config::Manual: + return QCoreApplication::translate( + context, "Manual partitioning on disk %1 (%2)." ) .arg( info.deviceNode ) .arg( info.deviceName ); } @@ -201,7 +211,9 @@ static QString diskDescription( int listLength, const PartitionCoreModule::Summa } else // multiple disk previews! { - return QCoreApplication::translate( context, "Disk %1 (%2)" ).arg( info.deviceNode ).arg( info.deviceName ) ; + return QCoreApplication::translate( context, "Disk %1 (%2)" ) + .arg( info.deviceNode ) + .arg( info.deviceName ); } } @@ -216,7 +228,7 @@ PartitionViewStep::prettyStatus() const cDebug() << "Summary for Partition" << list.length() << choice; if ( list.length() > 1 ) // There are changes on more than one disk { - modeText = modeDescription(choice); + modeText = modeDescription( choice ); } for ( const auto& info : list ) @@ -501,7 +513,7 @@ PartitionViewStep::onLeave() #else PartitionTable::FlagEsp #endif - ); + ); Partition* esp = m_core->findPartitionByMountPoint( espMountPoint ); QString message; @@ -644,8 +656,7 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap ) QFuture< void > future = QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule ); m_future->setFuture( future ); - m_core->initLayout( m_config->defaultFsType(), - configurationMap.value( "partitionLayout" ).toList() ); + m_core->initLayout( m_config->defaultFsType(), configurationMap.value( "partitionLayout" ).toList() ); } diff --git a/src/modules/partition/core/PartUtils.cpp b/src/modules/partition/core/PartUtils.cpp index 4beac0db8..8792cb787 100644 --- a/src/modules/partition/core/PartUtils.cpp +++ b/src/modules/partition/core/PartUtils.cpp @@ -447,31 +447,31 @@ isEfiSystem() } bool -isEfiFilesystemSuitable(const Partition* candidate) +isEfiFilesystemSuitable( const Partition* candidate ) { auto type = candidate->fileSystem().type(); auto size = candidate->capacity(); // bytes using CalamaresUtils::Units::operator""_MiB; - switch( type ) + switch ( type ) { - case FileSystem::Type::Fat32: - if ( size >= 300_MiB ) - { - return true; - } - cWarning() << "FAT32 filesystem is too small (" << size << "bytes)"; - return false; + case FileSystem::Type::Fat32: + if ( size >= 300_MiB ) + { + return true; + } + cWarning() << "FAT32 filesystem is too small (" << size << "bytes)"; + return false; #ifdef WITH_KPMCORE4API - case FileSystem::Type::Fat12: + case FileSystem::Type::Fat12: #endif - case FileSystem::Type::Fat16: - cWarning() << "FAT12 and FAT16 are probably not supported by EFI"; - return false; - default: - cWarning() << "EFI boot partition must be FAT32"; - return false; + case FileSystem::Type::Fat16: + cWarning() << "FAT12 and FAT16 are probably not supported by EFI"; + return false; + default: + cWarning() << "EFI boot partition must be FAT32"; + return false; } } diff --git a/src/modules/partition/core/PartitionLayout.cpp b/src/modules/partition/core/PartitionLayout.cpp index 233f5117a..8ae904e92 100644 --- a/src/modules/partition/core/PartitionLayout.cpp +++ b/src/modules/partition/core/PartitionLayout.cpp @@ -138,61 +138,63 @@ PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& confi } void -PartitionLayout::setDefaultFsType(FileSystem::Type defaultFsType) +PartitionLayout::setDefaultFsType( FileSystem::Type defaultFsType ) { using FileSystem = FileSystem::Type; switch ( defaultFsType ) { - case FileSystem::Unknown: - case FileSystem::Unformatted: - case FileSystem::Extended: - case FileSystem::LinuxSwap: - case FileSystem::Luks: - case FileSystem::Ocfs2: - case FileSystem::Lvm2_PV: - case FileSystem::Udf: - case FileSystem::Iso9660: + case FileSystem::Unknown: + case FileSystem::Unformatted: + case FileSystem::Extended: + case FileSystem::LinuxSwap: + case FileSystem::Luks: + case FileSystem::Ocfs2: + case FileSystem::Lvm2_PV: + case FileSystem::Udf: + case FileSystem::Iso9660: #ifdef WITH_KPMCORE4API - case FileSystem::Luks2: - case FileSystem::LinuxRaidMember: - case FileSystem::BitLocker: + case FileSystem::Luks2: + case FileSystem::LinuxRaidMember: + case FileSystem::BitLocker: #endif - // bad bad - cWarning() << "The selected default FS" << defaultFsType << "is not suitable." << "Using ext4 instead."; - defaultFsType = FileSystem::Ext4; - break; - case FileSystem::Ext2: - case FileSystem::Ext3: - case FileSystem::Ext4: - case FileSystem::Fat32: - case FileSystem::Ntfs: - case FileSystem::Reiser4: - case FileSystem::ReiserFS: - case FileSystem::Xfs: - case FileSystem::Jfs: - case FileSystem::Btrfs: - case FileSystem::Exfat: - case FileSystem::F2fs: - // ok - break; - case FileSystem::Fat16: - case FileSystem::Hfs: - case FileSystem::HfsPlus: - case FileSystem::Ufs: - case FileSystem::Hpfs: - case FileSystem::Zfs: - case FileSystem::Nilfs2: + // bad bad + cWarning() << "The selected default FS" << defaultFsType << "is not suitable." + << "Using ext4 instead."; + defaultFsType = FileSystem::Ext4; + break; + case FileSystem::Ext2: + case FileSystem::Ext3: + case FileSystem::Ext4: + case FileSystem::Fat32: + case FileSystem::Ntfs: + case FileSystem::Reiser4: + case FileSystem::ReiserFS: + case FileSystem::Xfs: + case FileSystem::Jfs: + case FileSystem::Btrfs: + case FileSystem::Exfat: + case FileSystem::F2fs: + // ok + break; + case FileSystem::Fat16: + case FileSystem::Hfs: + case FileSystem::HfsPlus: + case FileSystem::Ufs: + case FileSystem::Hpfs: + case FileSystem::Zfs: + case FileSystem::Nilfs2: #ifdef WITH_KPMCORE4API - case FileSystem::Fat12: - case FileSystem::Apfs: - case FileSystem::Minix: + case FileSystem::Fat12: + case FileSystem::Apfs: + case FileSystem::Minix: #endif - // 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 = FileSystem::Ext4; + // 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 = FileSystem::Ext4; } m_defaultFsType = defaultFsType; @@ -278,7 +280,7 @@ PartitionLayout::createPartitions( Device* dev, } } - auto correctFS = [d=m_defaultFsType]( FileSystem::Type t ) { return t == FileSystem::Type::Unknown ? d : t; }; + auto correctFS = [d = m_defaultFsType]( FileSystem::Type t ) { return t == FileSystem::Type::Unknown ? d : t; }; // Create the partitions. currentSector = firstSector; diff --git a/src/modules/partition/gui/ChoicePage.cpp b/src/modules/partition/gui/ChoicePage.cpp index 6438724e8..27c2cd710 100644 --- a/src/modules/partition/gui/ChoicePage.cpp +++ b/src/modules/partition/gui/ChoicePage.cpp @@ -270,11 +270,12 @@ ChoicePage::setupChoices() m_eraseButton->addOptionsComboBox( m_eraseSwapChoiceComboBox ); } - if ( m_config->eraseFsTypes().count() > 1) + 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 ); + 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 ); } From d2e11dd5d1f1fac9560426f9eb94d7f0a673284f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 10:36:05 +0200 Subject: [PATCH 10/87] [summaryq] Apply coding style --- src/modules/summaryq/Config.cpp | 54 +++++++++++++-------- src/modules/summaryq/Config.h | 30 ++++++------ src/modules/summaryq/SummaryQmlViewStep.cpp | 8 +-- src/modules/summaryq/SummaryQmlViewStep.h | 11 ++--- 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp index 64e43a0ba..8314802a9 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summaryq/Config.cpp @@ -20,15 +20,19 @@ #include "utils/Retranslator.h" #include "viewpages/ExecutionViewStep.h" -SummaryModel::SummaryModel(QObject* parent) : QAbstractListModel(parent) -{} +SummaryModel::SummaryModel( QObject* parent ) + : QAbstractListModel( parent ) +{ +} -QHash SummaryModel::roleNames() const +QHash< int, QByteArray > +SummaryModel::roleNames() const { return { { Qt::DisplayRole, "title" }, { Qt::UserRole, "message" } }; } -QVariant SummaryModel::data(const QModelIndex& index, int role) const +QVariant +SummaryModel::data( const QModelIndex& index, int role ) const { if ( !index.isValid() ) { @@ -38,12 +42,14 @@ QVariant SummaryModel::data(const QModelIndex& index, int role) const return role == Qt::DisplayRole ? item->title : item->message; } -int SummaryModel::rowCount(const QModelIndex&) const +int +SummaryModel::rowCount( const QModelIndex& ) const { return m_summary.count(); } -void SummaryModel::setSummary(const Calamares::ViewStepList& steps) +void +SummaryModel::setSummary( const Calamares::ViewStepList& steps ) { m_summary.clear(); Q_EMIT beginResetModel(); @@ -54,44 +60,50 @@ void SummaryModel::setSummary(const Calamares::ViewStepList& steps) QWidget* widget = step->createSummaryWidget(); if ( text.isEmpty() && !widget ) + { continue; + } - m_summary << new StepSummary {step->prettyName(), text}; - + m_summary << new StepSummary { step->prettyName(), text }; } Q_EMIT endResetModel(); } -Config::Config(QObject *parent) : QObject(parent) -, m_thisViewStep(static_cast(parent)) -, m_summary( new SummaryModel(this) ) +Config::Config( QObject* parent ) + : QObject( parent ) + , m_thisViewStep( static_cast< SummaryQmlViewStep* >( parent ) ) + , m_summary( new SummaryModel( this ) ) { m_title = m_thisViewStep->prettyName(); if ( Calamares::Settings::instance()->isSetupMode() ) - m_message =( tr( "This is an overview of what will happen once you start " - "the setup procedure." ) ); + m_message = ( tr( "This is an overview of what will happen once you start " + "the setup procedure." ) ); else m_message = ( tr( "This is an overview of what will happen once you start " - "the install procedure." ) ); + "the install procedure." ) ); } -void Config::componentComplete() +void +Config::componentComplete() { refresh(); } -void Config::refresh() +void +Config::refresh() { - m_summary->setSummary( stepsForSummary( Calamares::ViewManager::instance()->viewSteps() )); + m_summary->setSummary( stepsForSummary( Calamares::ViewManager::instance()->viewSteps() ) ); } -void Config::init() +void +Config::init() { refresh(); } -Calamares::ViewStepList Config::stepsForSummary( const Calamares::ViewStepList& allSteps ) const +Calamares::ViewStepList +Config::stepsForSummary( const Calamares::ViewStepList& allSteps ) const { Calamares::ViewStepList steps; for ( Calamares::ViewStep* step : allSteps ) @@ -103,12 +115,12 @@ Calamares::ViewStepList Config::stepsForSummary( const Calamares::ViewStepList& } if ( m_thisViewStep == step ) + { break; + } steps.append( step ); } return steps; } - - diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index 162ff2c5f..39732a9e5 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -11,10 +11,10 @@ #ifndef SUMMARY_CONFIG_H #define SUMMARY_CONFIG_H -#include -#include -#include #include "viewpages/ViewStep.h" +#include +#include +#include class SummaryQmlViewStep; @@ -26,44 +26,42 @@ struct StepSummary class SummaryModel : public QAbstractListModel { - Q_OBJECT + Q_OBJECT public: - explicit SummaryModel(QObject *parent = nullptr); + explicit SummaryModel( QObject* parent = nullptr ); int rowCount( const QModelIndex& = QModelIndex() ) const override; QVariant data( const QModelIndex& index, int role ) const override; - void setSummary(const Calamares::ViewStepList &steps); + void setSummary( const Calamares::ViewStepList& steps ); protected: QHash< int, QByteArray > roleNames() const override; + private: - QVector m_summary; + QVector< StepSummary* > m_summary; }; class Config : public QObject, public QQmlParserStatus { Q_OBJECT - Q_PROPERTY(QString message MEMBER m_message NOTIFY messageChanged CONSTANT) - Q_PROPERTY(QString title MEMBER m_title NOTIFY titleChanged CONSTANT) - Q_PROPERTY(SummaryModel * summaryModel READ summaryModel CONSTANT FINAL) + Q_PROPERTY( QString message MEMBER m_message NOTIFY messageChanged CONSTANT ) + Q_PROPERTY( QString title MEMBER m_title NOTIFY titleChanged CONSTANT ) + Q_PROPERTY( SummaryModel* summaryModel READ summaryModel CONSTANT FINAL ) public: - explicit Config(QObject *parent = nullptr); + explicit Config( QObject* parent = nullptr ); virtual void componentComplete() override; virtual void classBegin() override {} void refresh(); void init(); - SummaryModel * summaryModel() const - { - return m_summary; - } + SummaryModel* summaryModel() const { return m_summary; } private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; const SummaryQmlViewStep* m_thisViewStep; - SummaryModel *m_summary; + SummaryModel* m_summary; QString m_message; QString m_title; diff --git a/src/modules/summaryq/SummaryQmlViewStep.cpp b/src/modules/summaryq/SummaryQmlViewStep.cpp index 8f7360212..2ea159984 100644 --- a/src/modules/summaryq/SummaryQmlViewStep.cpp +++ b/src/modules/summaryq/SummaryQmlViewStep.cpp @@ -10,7 +10,7 @@ #include "SummaryQmlViewStep.h" -CALAMARES_PLUGIN_FACTORY_DEFINITION( SummaryQmlViewStepFactory, registerPlugin(); ) +CALAMARES_PLUGIN_FACTORY_DEFINITION( SummaryQmlViewStepFactory, registerPlugin< SummaryQmlViewStep >(); ) SummaryQmlViewStep::SummaryQmlViewStep( QObject* parent ) : Calamares::QmlViewStep( parent ) @@ -20,10 +20,7 @@ SummaryQmlViewStep::SummaryQmlViewStep( QObject* parent ) } -SummaryQmlViewStep::~SummaryQmlViewStep() -{ - -} +SummaryQmlViewStep::~SummaryQmlViewStep() {} QString SummaryQmlViewStep::prettyName() const @@ -72,4 +69,3 @@ SummaryQmlViewStep::onActivate() { m_config->init(); } - diff --git a/src/modules/summaryq/SummaryQmlViewStep.h b/src/modules/summaryq/SummaryQmlViewStep.h index f42ec9f5c..42febd145 100644 --- a/src/modules/summaryq/SummaryQmlViewStep.h +++ b/src/modules/summaryq/SummaryQmlViewStep.h @@ -12,9 +12,9 @@ #define SUMMARYQMLVIEWSTEP_H #include "Config.h" +#include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/QmlViewStep.h" -#include "DllMacro.h" #include @@ -41,15 +41,12 @@ public: void onActivate() override; - QObject * getConfig() override - { - return m_config; - } + QObject* getConfig() override { return m_config; } private: - Config *m_config; + Config* m_config; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( SummaryQmlViewStepFactory ) -#endif // SUMMARYQMLVIEWSTEP_H +#endif // SUMMARYQMLVIEWSTEP_H From 3b7c2b2221e2981f39a7aa363d31ae01d0d72169 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 11:02:24 +0200 Subject: [PATCH 11/87] [summaryq] Remove memory leak Don't use a vector of pointers, it is too easy (like when calling clear()) to leak memory. --- src/modules/summaryq/Config.cpp | 10 +++++----- src/modules/summaryq/Config.h | 12 ++++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp index 8314802a9..e356f0589 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summaryq/Config.cpp @@ -39,7 +39,7 @@ SummaryModel::data( const QModelIndex& index, int role ) const return QVariant(); } const auto item = m_summary.at( index.row() ); - return role == Qt::DisplayRole ? item->title : item->message; + return role == Qt::DisplayRole ? item.title : item.message; } int @@ -49,22 +49,22 @@ SummaryModel::rowCount( const QModelIndex& ) const } void -SummaryModel::setSummary( const Calamares::ViewStepList& steps ) +SummaryModel::setSummary( const Calamares::ViewStepList& steps, bool withWidgets ) { - m_summary.clear(); Q_EMIT beginResetModel(); + m_summary.clear(); for ( Calamares::ViewStep* step : steps ) { QString text = step->prettyStatus(); - QWidget* widget = step->createSummaryWidget(); + QWidget* widget = withWidgets ? step->createSummaryWidget() : nullptr; if ( text.isEmpty() && !widget ) { continue; } - m_summary << new StepSummary { step->prettyName(), text }; + m_summary << StepSummary { step->prettyName(), text, widget }; } Q_EMIT endResetModel(); } diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index 39732a9e5..acce67e18 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -18,10 +18,18 @@ class SummaryQmlViewStep; + +/** @brief Data for one step + * + * A step generally has a text description, but **may** have a + * QWidget. There is no ownership of the QWidget, that is assumed + * to be handed off to some owning parent-widget. + */ struct StepSummary { QString title; QString message; + QWidget* widget = nullptr; }; class SummaryModel : public QAbstractListModel @@ -32,13 +40,13 @@ public: int rowCount( const QModelIndex& = QModelIndex() ) const override; QVariant data( const QModelIndex& index, int role ) const override; - void setSummary( const Calamares::ViewStepList& steps ); + void setSummary( const Calamares::ViewStepList& steps, bool withWidgets = false ); protected: QHash< int, QByteArray > roleNames() const override; private: - QVector< StepSummary* > m_summary; + QVector< StepSummary > m_summary; }; class Config : public QObject, public QQmlParserStatus From a316f1b40df48bd3b35574b050940889730f6955 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 20:42:19 +0200 Subject: [PATCH 12/87] [summaryq] Repair translatable properties - doesn't make sense to have NOTIFY and CONSTANT - connect to translation signals - document the properties --- src/modules/summaryq/Config.cpp | 15 ++++++++++++++- src/modules/summaryq/Config.h | 10 ++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp index e356f0589..603ed460d 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summaryq/Config.cpp @@ -14,7 +14,6 @@ #include "Branding.h" #include "Settings.h" #include "ViewManager.h" - #include "utils/CalamaresUtilsGui.h" #include "utils/Logger.h" #include "utils/Retranslator.h" @@ -73,15 +72,29 @@ Config::Config( QObject* parent ) : QObject( parent ) , m_thisViewStep( static_cast< SummaryQmlViewStep* >( parent ) ) , m_summary( new SummaryModel( this ) ) + +{ + CALAMARES_RETRANSLATE_SLOT( &Config::retranslate ); + retranslate(); +} + +void +Config::retranslate() { m_title = m_thisViewStep->prettyName(); if ( Calamares::Settings::instance()->isSetupMode() ) + { m_message = ( tr( "This is an overview of what will happen once you start " "the setup procedure." ) ); + } else + { m_message = ( tr( "This is an overview of what will happen once you start " "the install procedure." ) ); + } + Q_EMIT titleChanged(); + Q_EMIT messageChanged(); } void diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index acce67e18..f8c00173d 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -52,8 +52,12 @@ private: class Config : public QObject, public QQmlParserStatus { Q_OBJECT - Q_PROPERTY( QString message MEMBER m_message NOTIFY messageChanged CONSTANT ) - Q_PROPERTY( QString title MEMBER m_title NOTIFY titleChanged CONSTANT ) + + ///@brief Name of this summary (generally, "Summary") + Q_PROPERTY( QString title MEMBER m_title NOTIFY titleChanged ) + ///@brief Description of what the summary means + Q_PROPERTY( QString message MEMBER m_message NOTIFY messageChanged ) + Q_PROPERTY( SummaryModel* summaryModel READ summaryModel CONSTANT FINAL ) public: @@ -68,6 +72,8 @@ public: private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; + void retranslate(); + const SummaryQmlViewStep* m_thisViewStep; SummaryModel* m_summary; From a658e885755d1ef8f09a556bb321427b95272ec6 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 21:25:57 +0200 Subject: [PATCH 13/87] [summaryq] Nothing in the Config is specific to this QML view-step --- src/modules/summaryq/Config.cpp | 2 +- src/modules/summaryq/Config.h | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp index 603ed460d..e0f855ab5 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summaryq/Config.cpp @@ -68,7 +68,7 @@ SummaryModel::setSummary( const Calamares::ViewStepList& steps, bool withWidgets Q_EMIT endResetModel(); } -Config::Config( QObject* parent ) +Config::Config( Calamares::ViewStep* parent ) : QObject( parent ) , m_thisViewStep( static_cast< SummaryQmlViewStep* >( parent ) ) , m_summary( new SummaryModel( this ) ) diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index f8c00173d..97eaeadd5 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -12,13 +12,11 @@ #define SUMMARY_CONFIG_H #include "viewpages/ViewStep.h" + #include #include #include -class SummaryQmlViewStep; - - /** @brief Data for one step * * A step generally has a text description, but **may** have a @@ -61,7 +59,7 @@ class Config : public QObject, public QQmlParserStatus Q_PROPERTY( SummaryModel* summaryModel READ summaryModel CONSTANT FINAL ) public: - explicit Config( QObject* parent = nullptr ); + explicit Config( Calamares::ViewStep* parent = nullptr ); virtual void componentComplete() override; virtual void classBegin() override {} @@ -74,7 +72,7 @@ private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; void retranslate(); - const SummaryQmlViewStep* m_thisViewStep; + const Calamares::ViewStep* m_thisViewStep; SummaryModel* m_summary; QString m_message; From 792ba8c0af3faaabf8bd022ee232e01f54ff2f66 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 21:28:46 +0200 Subject: [PATCH 14/87] [summaryq] Nothing uses the derived type of the model (This assertion may be dialed back if the Config object is used in the summary ViewStep, which will want to get at the widget pointers, but that's for later) --- src/modules/summaryq/Config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index 97eaeadd5..ec804321b 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -56,7 +56,7 @@ class Config : public QObject, public QQmlParserStatus ///@brief Description of what the summary means Q_PROPERTY( QString message MEMBER m_message NOTIFY messageChanged ) - Q_PROPERTY( SummaryModel* summaryModel READ summaryModel CONSTANT FINAL ) + Q_PROPERTY( QAbstractListModel* summaryModel READ summaryModel CONSTANT FINAL ) public: explicit Config( Calamares::ViewStep* parent = nullptr ); @@ -66,7 +66,7 @@ public: void refresh(); void init(); - SummaryModel* summaryModel() const { return m_summary; } + QAbstractListModel* summaryModel() const { return m_summary; } private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; From 768760793ab822d68ff844ff9fccf1d364f57c53 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 22:04:01 +0200 Subject: [PATCH 15/87] [summaryq] Hide internals of building the summary model --- src/modules/summaryq/Config.cpp | 32 +++++++++++++++++--------------- src/modules/summaryq/Config.h | 15 +++++++++++++-- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp index e0f855ab5..e404c1f62 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summaryq/Config.cpp @@ -48,7 +48,7 @@ SummaryModel::rowCount( const QModelIndex& ) const } void -SummaryModel::setSummary( const Calamares::ViewStepList& steps, bool withWidgets ) +SummaryModel::setSummaryList( const Calamares::ViewStepList& steps, bool withWidgets ) { Q_EMIT beginResetModel(); m_summary.clear(); @@ -105,28 +105,24 @@ Config::componentComplete() void Config::refresh() -{ - m_summary->setSummary( stepsForSummary( Calamares::ViewManager::instance()->viewSteps() ) ); -} - -void -Config::init() -{ - refresh(); -} - -Calamares::ViewStepList -Config::stepsForSummary( const Calamares::ViewStepList& allSteps ) const { Calamares::ViewStepList steps; - for ( Calamares::ViewStep* step : allSteps ) + for ( Calamares::ViewStep* step : Calamares::ViewManager::instance()->viewSteps() ) { + // *Assume* that if there's an exec step in the sequence, + // we don't need a summary for steps before it. This works in + // practice if there's a summary step before each exec -- + // and in practice, there's only one of each. if ( qobject_cast< Calamares::ExecutionViewStep* >( step ) ) { steps.clear(); continue; } + // Having reached the parent view-step of the Config object, + // we know we're providing a summary of steps up until this + // view step, so we now have steps since the previous exec, up + // to this summary. if ( m_thisViewStep == step ) { break; @@ -135,5 +131,11 @@ Config::stepsForSummary( const Calamares::ViewStepList& allSteps ) const steps.append( step ); } - return steps; + m_summary->setSummaryList( steps ); +} + +void +Config::init() +{ + refresh(); } diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index ec804321b..08f2c0f16 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -17,6 +17,8 @@ #include #include +class Config; + /** @brief Data for one step * * A step generally has a text description, but **may** have a @@ -33,17 +35,26 @@ struct StepSummary class SummaryModel : public QAbstractListModel { Q_OBJECT + friend class Config; + public: explicit SummaryModel( QObject* parent = nullptr ); int rowCount( const QModelIndex& = QModelIndex() ) const override; QVariant data( const QModelIndex& index, int role ) const override; - void setSummary( const Calamares::ViewStepList& steps, bool withWidgets = false ); - protected: QHash< int, QByteArray > roleNames() const override; private: + /** @brief Sets the model data from @p steps + * + * Replaces the list of summaries with summaries given by + * the jobs and ViewSteps objects in @p steps. If @p withWidgets + * is @c true, then also queries for widget summaries alongside + * the text summaries for each step. + */ + void setSummaryList( const Calamares::ViewStepList& steps, bool withWidgets = false ); + QVector< StepSummary > m_summary; }; From 235db9f9619309cafa0087e4281bcb7f9c89606a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 13 Jul 2021 22:07:06 +0200 Subject: [PATCH 16/87] [summaryq] No need to be a ParserStatus object Config classes, which intend to be shared between widgets- and QML-based view-steps, should not tie closely to internals. None of the ParserStatus methods are used in a meaningful way (init() can be called by the view step). --- src/modules/summaryq/Config.cpp | 14 +------------- src/modules/summaryq/Config.h | 6 ++---- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summaryq/Config.cpp index e404c1f62..0f98891a6 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summaryq/Config.cpp @@ -98,13 +98,7 @@ Config::retranslate() } void -Config::componentComplete() -{ - refresh(); -} - -void -Config::refresh() +Config::init() { Calamares::ViewStepList steps; for ( Calamares::ViewStep* step : Calamares::ViewManager::instance()->viewSteps() ) @@ -133,9 +127,3 @@ Config::refresh() m_summary->setSummaryList( steps ); } - -void -Config::init() -{ - refresh(); -} diff --git a/src/modules/summaryq/Config.h b/src/modules/summaryq/Config.h index 08f2c0f16..524aeee8e 100644 --- a/src/modules/summaryq/Config.h +++ b/src/modules/summaryq/Config.h @@ -58,7 +58,7 @@ private: QVector< StepSummary > m_summary; }; -class Config : public QObject, public QQmlParserStatus +class Config : public QObject { Q_OBJECT @@ -71,10 +71,8 @@ class Config : public QObject, public QQmlParserStatus public: explicit Config( Calamares::ViewStep* parent = nullptr ); - virtual void componentComplete() override; - virtual void classBegin() override {} - void refresh(); + ///@brief Called later, to load the model once all viewsteps are there void init(); QAbstractListModel* summaryModel() const { return m_summary; } From 6c7e7a6d55cc8d0307a093d6dbed832708f0954d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 14:45:55 +0200 Subject: [PATCH 17/87] [summaryq] a handful of code-style fixes --- src/modules/summaryq/SummaryQmlViewStep.cpp | 6 ++++-- src/modules/summaryq/SummaryQmlViewStep.h | 7 ++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/modules/summaryq/SummaryQmlViewStep.cpp b/src/modules/summaryq/SummaryQmlViewStep.cpp index 2ea159984..0be2b1615 100644 --- a/src/modules/summaryq/SummaryQmlViewStep.cpp +++ b/src/modules/summaryq/SummaryQmlViewStep.cpp @@ -57,15 +57,17 @@ SummaryQmlViewStep::isAtEnd() const } -QList< Calamares::job_ptr > +Calamares::JobList SummaryQmlViewStep::jobs() const { - return QList< Calamares::job_ptr >(); + return {}; } void SummaryQmlViewStep::onActivate() { + // Collect the steps before this one: those need to have their + // summary (text or widget) displayed. m_config->init(); } diff --git a/src/modules/summaryq/SummaryQmlViewStep.h b/src/modules/summaryq/SummaryQmlViewStep.h index 42febd145..8668d0afe 100644 --- a/src/modules/summaryq/SummaryQmlViewStep.h +++ b/src/modules/summaryq/SummaryQmlViewStep.h @@ -12,14 +12,11 @@ #define SUMMARYQMLVIEWSTEP_H #include "Config.h" + #include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/QmlViewStep.h" -#include - -class SummaryPage; - class PLUGINDLLEXPORT SummaryQmlViewStep : public Calamares::QmlViewStep { Q_OBJECT @@ -37,7 +34,7 @@ public: bool isAtBeginning() const override; bool isAtEnd() const override; - QList< Calamares::job_ptr > jobs() const override; + Calamares::JobList jobs() const override; void onActivate() override; From f8c3b76367c6ba9da1fffde0ea0b752e64c61b8a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 14:49:08 +0200 Subject: [PATCH 18/87] [summary] Stylistic fixes before moving Config objects around --- src/modules/summary/SummaryViewStep.cpp | 4 ++-- src/modules/summary/SummaryViewStep.h | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/modules/summary/SummaryViewStep.cpp b/src/modules/summary/SummaryViewStep.cpp index c5b6841ed..3c3a48ff2 100644 --- a/src/modules/summary/SummaryViewStep.cpp +++ b/src/modules/summary/SummaryViewStep.cpp @@ -72,10 +72,10 @@ SummaryViewStep::isAtEnd() const } -QList< Calamares::job_ptr > +Calamares::JobList SummaryViewStep::jobs() const { - return QList< Calamares::job_ptr >(); + return {}; } diff --git a/src/modules/summary/SummaryViewStep.h b/src/modules/summary/SummaryViewStep.h index c89efc42f..0564e34aa 100644 --- a/src/modules/summary/SummaryViewStep.h +++ b/src/modules/summary/SummaryViewStep.h @@ -10,13 +10,10 @@ #ifndef SUMMARYPAGEPLUGIN_H #define SUMMARYPAGEPLUGIN_H -#include - +#include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/ViewStep.h" -#include "DllMacro.h" - class SummaryPage; class PLUGINDLLEXPORT SummaryViewStep : public Calamares::ViewStep @@ -37,7 +34,7 @@ public: bool isAtBeginning() const override; bool isAtEnd() const override; - QList< Calamares::job_ptr > jobs() const override; + Calamares::JobList jobs() const override; void onActivate() override; void onLeave() override; From aba212d70096a83480bf06c3d9b6146e77abc708 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 14:57:09 +0200 Subject: [PATCH 19/87] [summary*] Move Config to the non-QML module, so it can be shared --- src/modules/summary/CMakeLists.txt | 3 ++- src/modules/{summaryq => summary}/Config.cpp | 3 +-- src/modules/{summaryq => summary}/Config.h | 0 src/modules/summaryq/CMakeLists.txt | 5 ++--- 4 files changed, 5 insertions(+), 6 deletions(-) rename src/modules/{summaryq => summary}/Config.cpp (97%) rename src/modules/{summaryq => summary}/Config.h (100%) diff --git a/src/modules/summary/CMakeLists.txt b/src/modules/summary/CMakeLists.txt index 57dc731cb..746b22493 100644 --- a/src/modules/summary/CMakeLists.txt +++ b/src/modules/summary/CMakeLists.txt @@ -8,8 +8,9 @@ calamares_add_plugin( summary TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES - SummaryViewStep.cpp + Config.cpp SummaryPage.cpp + SummaryViewStep.cpp UI LINK_PRIVATE_LIBRARIES calamaresui diff --git a/src/modules/summaryq/Config.cpp b/src/modules/summary/Config.cpp similarity index 97% rename from src/modules/summaryq/Config.cpp rename to src/modules/summary/Config.cpp index 0f98891a6..bbbb103c3 100644 --- a/src/modules/summaryq/Config.cpp +++ b/src/modules/summary/Config.cpp @@ -9,7 +9,6 @@ */ #include "Config.h" -#include "SummaryQmlViewStep.h" #include "Branding.h" #include "Settings.h" @@ -70,7 +69,7 @@ SummaryModel::setSummaryList( const Calamares::ViewStepList& steps, bool withWid Config::Config( Calamares::ViewStep* parent ) : QObject( parent ) - , m_thisViewStep( static_cast< SummaryQmlViewStep* >( parent ) ) + , m_thisViewStep( parent ) , m_summary( new SummaryModel( this ) ) { diff --git a/src/modules/summaryq/Config.h b/src/modules/summary/Config.h similarity index 100% rename from src/modules/summaryq/Config.h rename to src/modules/summary/Config.h diff --git a/src/modules/summaryq/CMakeLists.txt b/src/modules/summaryq/CMakeLists.txt index 0df854140..cdf520b24 100644 --- a/src/modules/summaryq/CMakeLists.txt +++ b/src/modules/summaryq/CMakeLists.txt @@ -4,15 +4,14 @@ if( NOT WITH_QML ) endif() set( _summary ${CMAKE_CURRENT_SOURCE_DIR}/../summary ) -include_directories( ${_finished} ) +include_directories( ${_summary} ) -include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ) calamares_add_plugin( summaryq TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES SummaryQmlViewStep.cpp - Config.cpp + ${_summary}/Config.cpp UI RESOURCES summaryq.qrc From 9eee00c286dc5d88efb1e8d1281b532cbc1c9c94 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 15:17:35 +0200 Subject: [PATCH 20/87] [summary] Create Config object - Create Config object, even if it's not used just yet - Introduce onLeave() for the Page, better name than (re-)creating the content (!?) when leaving --- src/modules/summary/SummaryPage.cpp | 7 +++++++ src/modules/summary/SummaryPage.h | 5 ++++- src/modules/summary/SummaryViewStep.cpp | 5 ++++- src/modules/summary/SummaryViewStep.h | 5 ++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/modules/summary/SummaryPage.cpp b/src/modules/summary/SummaryPage.cpp index b56793e7e..083a6d172 100644 --- a/src/modules/summary/SummaryPage.cpp +++ b/src/modules/summary/SummaryPage.cpp @@ -166,6 +166,13 @@ SummaryPage::createContentWidget() CalamaresUtils::unmarginLayout( m_layout ); } +void +SummaryPage::onLeave() +{ + delete m_contentWidget; + m_contentWidget = nullptr; +} + QLabel* SummaryPage::createTitleLabel( const QString& text ) const { diff --git a/src/modules/summary/SummaryPage.h b/src/modules/summary/SummaryPage.h index 1adb67017..d7da7bfe0 100644 --- a/src/modules/summary/SummaryPage.h +++ b/src/modules/summary/SummaryPage.h @@ -44,11 +44,14 @@ class SummaryPage : public QWidget public: explicit SummaryPage( const SummaryViewStep* thisViewStep, QWidget* parent = nullptr ); + /// @brief Create contents showing all of the summary void onActivate(); - void createContentWidget(); + /// @brief Clean up the widgets + void onLeave(); private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; + void createContentWidget(); const SummaryViewStep* m_thisViewStep; diff --git a/src/modules/summary/SummaryViewStep.cpp b/src/modules/summary/SummaryViewStep.cpp index 3c3a48ff2..04af6b524 100644 --- a/src/modules/summary/SummaryViewStep.cpp +++ b/src/modules/summary/SummaryViewStep.cpp @@ -16,6 +16,7 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( SummaryViewStepFactory, registerPlugin< Sum SummaryViewStep::SummaryViewStep( QObject* parent ) : Calamares::ViewStep( parent ) , m_widget( new SummaryPage( this ) ) + , m_config( new Config( this ) ) { emit nextStatusChanged( true ); } @@ -27,6 +28,7 @@ SummaryViewStep::~SummaryViewStep() { m_widget->deleteLater(); } + delete m_config; } @@ -82,6 +84,7 @@ SummaryViewStep::jobs() const void SummaryViewStep::onActivate() { + m_config->init(); m_widget->onActivate(); } @@ -89,5 +92,5 @@ SummaryViewStep::onActivate() void SummaryViewStep::onLeave() { - m_widget->createContentWidget(); + m_widget->onLeave(); } diff --git a/src/modules/summary/SummaryViewStep.h b/src/modules/summary/SummaryViewStep.h index 0564e34aa..f1b81b2cb 100644 --- a/src/modules/summary/SummaryViewStep.h +++ b/src/modules/summary/SummaryViewStep.h @@ -10,6 +10,8 @@ #ifndef SUMMARYPAGEPLUGIN_H #define SUMMARYPAGEPLUGIN_H +#include "Config.h" + #include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/ViewStep.h" @@ -40,7 +42,8 @@ public: void onLeave() override; private: - SummaryPage* m_widget; + SummaryPage* m_widget = nullptr; + Config* m_config = nullptr; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( SummaryViewStepFactory ) From 4e588584d7780408331133bf25c71907952e7798 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 15:49:03 +0200 Subject: [PATCH 21/87] [summary] Hook up Config message to page - the Page displays a simple message describing what the summary is all about; Config has the same message, use that. Needed some re-jigging to get the signals and slots right. --- src/modules/summary/Config.cpp | 4 ++-- src/modules/summary/Config.h | 15 +++++++++------ src/modules/summary/SummaryPage.cpp | 10 ++++------ src/modules/summary/SummaryPage.h | 7 +++++-- src/modules/summary/SummaryViewStep.cpp | 2 +- src/modules/summary/SummaryViewStep.h | 2 +- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/modules/summary/Config.cpp b/src/modules/summary/Config.cpp index bbbb103c3..18ce9354d 100644 --- a/src/modules/summary/Config.cpp +++ b/src/modules/summary/Config.cpp @@ -92,8 +92,8 @@ Config::retranslate() m_message = ( tr( "This is an overview of what will happen once you start " "the install procedure." ) ); } - Q_EMIT titleChanged(); - Q_EMIT messageChanged(); + Q_EMIT titleChanged( m_title ); + Q_EMIT messageChanged( m_message ); } void diff --git a/src/modules/summary/Config.h b/src/modules/summary/Config.h index 524aeee8e..19cd6212c 100644 --- a/src/modules/summary/Config.h +++ b/src/modules/summary/Config.h @@ -63,9 +63,9 @@ class Config : public QObject Q_OBJECT ///@brief Name of this summary (generally, "Summary") - Q_PROPERTY( QString title MEMBER m_title NOTIFY titleChanged ) + Q_PROPERTY( QString title READ title NOTIFY titleChanged ) ///@brief Description of what the summary means - Q_PROPERTY( QString message MEMBER m_message NOTIFY messageChanged ) + Q_PROPERTY( QString message READ message NOTIFY messageChanged ) Q_PROPERTY( QAbstractListModel* summaryModel READ summaryModel CONSTANT FINAL ) @@ -77,6 +77,9 @@ public: QAbstractListModel* summaryModel() const { return m_summary; } + QString title() const { return m_title; } + QString message() const { return m_message; } + private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; void retranslate(); @@ -84,11 +87,11 @@ private: const Calamares::ViewStep* m_thisViewStep; SummaryModel* m_summary; - QString m_message; QString m_title; + QString m_message; -signals: - void messageChanged(); - void titleChanged(); +Q_SIGNALS: + void titleChanged( QString title ); + void messageChanged( QString message ); }; #endif diff --git a/src/modules/summary/SummaryPage.cpp b/src/modules/summary/SummaryPage.cpp index 083a6d172..084b366e8 100644 --- a/src/modules/summary/SummaryPage.cpp +++ b/src/modules/summary/SummaryPage.cpp @@ -29,7 +29,7 @@ static const int SECTION_SPACING = 12; -SummaryPage::SummaryPage( const SummaryViewStep* thisViewStep, QWidget* parent ) +SummaryPage::SummaryPage( Config* config, const SummaryViewStep* thisViewStep, QWidget* parent ) : QWidget() , m_thisViewStep( thisViewStep ) , m_contentWidget( nullptr ) @@ -37,6 +37,7 @@ SummaryPage::SummaryPage( const SummaryViewStep* thisViewStep, QWidget* parent ) { Q_UNUSED( parent ) + this->setObjectName( "summaryStep" ); Q_ASSERT( m_thisViewStep ); @@ -45,11 +46,8 @@ SummaryPage::SummaryPage( const SummaryViewStep* thisViewStep, QWidget* parent ) QLabel* headerLabel = new QLabel( this ); headerLabel->setObjectName( "summaryTitle" ); - CALAMARES_RETRANSLATE( if ( Calamares::Settings::instance()->isSetupMode() ) - headerLabel->setText( tr( "This is an overview of what will happen once you start " - "the setup procedure." ) ); - else headerLabel->setText( tr( "This is an overview of what will happen once you start " - "the install procedure." ) ); ); + headerLabel->setText( config->message() ); + connect( config, &Config::messageChanged, headerLabel, &QLabel::setText ); layout->addWidget( headerLabel ); layout->addWidget( m_scrollArea ); m_scrollArea->setWidgetResizable( true ); diff --git a/src/modules/summary/SummaryPage.h b/src/modules/summary/SummaryPage.h index d7da7bfe0..6e5f25c05 100644 --- a/src/modules/summary/SummaryPage.h +++ b/src/modules/summary/SummaryPage.h @@ -14,10 +14,13 @@ #include +class Config; +class SummaryViewStep; + class QLabel; class QScrollArea; class QVBoxLayout; -class SummaryViewStep; + /** @brief Provide a summary view with to-be-done action descriptions. * @@ -42,7 +45,7 @@ class SummaryPage : public QWidget { Q_OBJECT public: - explicit SummaryPage( const SummaryViewStep* thisViewStep, QWidget* parent = nullptr ); + explicit SummaryPage( Config* config, const SummaryViewStep* thisViewStep, QWidget* parent = nullptr ); /// @brief Create contents showing all of the summary void onActivate(); diff --git a/src/modules/summary/SummaryViewStep.cpp b/src/modules/summary/SummaryViewStep.cpp index 04af6b524..e29fbae03 100644 --- a/src/modules/summary/SummaryViewStep.cpp +++ b/src/modules/summary/SummaryViewStep.cpp @@ -15,8 +15,8 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( SummaryViewStepFactory, registerPlugin< Sum SummaryViewStep::SummaryViewStep( QObject* parent ) : Calamares::ViewStep( parent ) - , m_widget( new SummaryPage( this ) ) , m_config( new Config( this ) ) + , m_widget( new SummaryPage( m_config, this ) ) { emit nextStatusChanged( true ); } diff --git a/src/modules/summary/SummaryViewStep.h b/src/modules/summary/SummaryViewStep.h index f1b81b2cb..e2ee0566e 100644 --- a/src/modules/summary/SummaryViewStep.h +++ b/src/modules/summary/SummaryViewStep.h @@ -42,8 +42,8 @@ public: void onLeave() override; private: - SummaryPage* m_widget = nullptr; Config* m_config = nullptr; + SummaryPage* m_widget = nullptr; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( SummaryViewStepFactory ) From 5de99d53d20e92b363e4556e6f9b41d2e5953f2a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 16:00:53 +0200 Subject: [PATCH 22/87] [summary] Move title to Config object Rather than Config asking its (owning) ViewStep what the title is -- all existing implementations have a prettyName() for that -- move the title into Config and re-do-the ViewSteps to use it. Rename init() to something meaningful. --- src/modules/summary/Config.cpp | 17 ++++++++--------- src/modules/summary/Config.h | 5 ++--- src/modules/summary/SummaryViewStep.cpp | 4 ++-- src/modules/summaryq/SummaryQmlViewStep.cpp | 4 ++-- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/modules/summary/Config.cpp b/src/modules/summary/Config.cpp index 18ce9354d..5110e09a6 100644 --- a/src/modules/summary/Config.cpp +++ b/src/modules/summary/Config.cpp @@ -67,9 +67,8 @@ SummaryModel::setSummaryList( const Calamares::ViewStepList& steps, bool withWid Q_EMIT endResetModel(); } -Config::Config( Calamares::ViewStep* parent ) +Config::Config( QObject* parent ) : QObject( parent ) - , m_thisViewStep( parent ) , m_summary( new SummaryModel( this ) ) { @@ -80,24 +79,24 @@ Config::Config( Calamares::ViewStep* parent ) void Config::retranslate() { - m_title = m_thisViewStep->prettyName(); + m_title = tr( "Summary" ); if ( Calamares::Settings::instance()->isSetupMode() ) { - m_message = ( tr( "This is an overview of what will happen once you start " - "the setup procedure." ) ); + m_message = tr( "This is an overview of what will happen once you start " + "the setup procedure." ); } else { - m_message = ( tr( "This is an overview of what will happen once you start " - "the install procedure." ) ); + m_message = tr( "This is an overview of what will happen once you start " + "the install procedure." ); } Q_EMIT titleChanged( m_title ); Q_EMIT messageChanged( m_message ); } void -Config::init() +Config::collectSummaries( const Calamares::ViewStep* upToHere ) { Calamares::ViewStepList steps; for ( Calamares::ViewStep* step : Calamares::ViewManager::instance()->viewSteps() ) @@ -116,7 +115,7 @@ Config::init() // we know we're providing a summary of steps up until this // view step, so we now have steps since the previous exec, up // to this summary. - if ( m_thisViewStep == step ) + if ( upToHere == step ) { break; } diff --git a/src/modules/summary/Config.h b/src/modules/summary/Config.h index 19cd6212c..15604d933 100644 --- a/src/modules/summary/Config.h +++ b/src/modules/summary/Config.h @@ -70,10 +70,10 @@ class Config : public QObject Q_PROPERTY( QAbstractListModel* summaryModel READ summaryModel CONSTANT FINAL ) public: - explicit Config( Calamares::ViewStep* parent = nullptr ); + explicit Config( QObject* parent = nullptr ); ///@brief Called later, to load the model once all viewsteps are there - void init(); + void collectSummaries( const Calamares::ViewStep* upToHere ); QAbstractListModel* summaryModel() const { return m_summary; } @@ -84,7 +84,6 @@ private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; void retranslate(); - const Calamares::ViewStep* m_thisViewStep; SummaryModel* m_summary; QString m_title; diff --git a/src/modules/summary/SummaryViewStep.cpp b/src/modules/summary/SummaryViewStep.cpp index e29fbae03..d4e439ae3 100644 --- a/src/modules/summary/SummaryViewStep.cpp +++ b/src/modules/summary/SummaryViewStep.cpp @@ -35,7 +35,7 @@ SummaryViewStep::~SummaryViewStep() QString SummaryViewStep::prettyName() const { - return tr( "Summary" ); + return m_config->title(); } @@ -84,7 +84,7 @@ SummaryViewStep::jobs() const void SummaryViewStep::onActivate() { - m_config->init(); + m_config->collectSummaries( this ); m_widget->onActivate(); } diff --git a/src/modules/summaryq/SummaryQmlViewStep.cpp b/src/modules/summaryq/SummaryQmlViewStep.cpp index 0be2b1615..23e18a861 100644 --- a/src/modules/summaryq/SummaryQmlViewStep.cpp +++ b/src/modules/summaryq/SummaryQmlViewStep.cpp @@ -25,7 +25,7 @@ SummaryQmlViewStep::~SummaryQmlViewStep() {} QString SummaryQmlViewStep::prettyName() const { - return tr( "Summary" ); + return m_config->title(); } @@ -69,5 +69,5 @@ SummaryQmlViewStep::onActivate() { // Collect the steps before this one: those need to have their // summary (text or widget) displayed. - m_config->init(); + m_config->collectSummaries( this ); } From d6fe30dfdb240270a8004dd8c6c092956d6973ac Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 16:21:59 +0200 Subject: [PATCH 23/87] [summary] No need for widget-creation to be a method of the Page --- src/modules/summary/SummaryPage.cpp | 62 +++++++++++++++-------------- src/modules/summary/SummaryPage.h | 3 -- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/modules/summary/SummaryPage.cpp b/src/modules/summary/SummaryPage.cpp index 084b366e8..1c44a3063 100644 --- a/src/modules/summary/SummaryPage.cpp +++ b/src/modules/summary/SummaryPage.cpp @@ -61,6 +61,29 @@ SummaryPage::SummaryPage( Config* config, const SummaryViewStep* thisViewStep, Q } +static QLabel* +createTitleLabel( const QString& text, const QFont& titleFont ) +{ + QLabel* label = new QLabel( text ); + label->setObjectName( "summaryItemTitle" ); + label->setFont( titleFont ); + label->setContentsMargins( 0, 0, 0, 0 ); + + return label; +} + +static QLabel* +createBodyLabel( const QString& text, const QPalette& bodyPalette ) +{ + QLabel* label = new QLabel; + label->setObjectName( "summaryItemBody" ); + label->setMargin( CalamaresUtils::defaultFontHeight() / 2 ); + label->setAutoFillBackground( true ); + label->setPalette( bodyPalette ); + label->setText( text ); + return label; +} + // Adds a widget for those ViewSteps that want a summary; // see SummaryPage documentation and also ViewStep docs. void @@ -68,6 +91,13 @@ SummaryPage::onActivate() { createContentWidget(); + QFont titleFont = font(); + titleFont.setWeight( QFont::Light ); + titleFont.setPointSize( CalamaresUtils::defaultFontSize() * 2 ); + + QPalette bodyPalette( palette() ); + bodyPalette.setColor( WindowBackground, palette().window().color().lighter( 108 ) ); + bool first = true; const Calamares::ViewStepList steps = stepsForSummary( Calamares::ViewManager::instance()->viewSteps() ); @@ -90,7 +120,7 @@ SummaryPage::onActivate() m_layout->addSpacing( SECTION_SPACING ); } - m_layout->addWidget( createTitleLabel( step->prettyName() ) ); + m_layout->addWidget( createTitleLabel( step->prettyName(), titleFont ) ); QHBoxLayout* itemBodyLayout = new QHBoxLayout; m_layout->addSpacing( CalamaresUtils::defaultFontHeight() / 2 ); m_layout->addLayout( itemBodyLayout ); @@ -100,7 +130,7 @@ SummaryPage::onActivate() CalamaresUtils::unmarginLayout( itemBodyLayout ); if ( !text.isEmpty() ) { - itemBodyCoreLayout->addWidget( createBodyLabel( text ) ); + itemBodyCoreLayout->addWidget( createBodyLabel( text, bodyPalette ) ); } if ( widget ) { @@ -170,31 +200,3 @@ SummaryPage::onLeave() delete m_contentWidget; m_contentWidget = nullptr; } - -QLabel* -SummaryPage::createTitleLabel( const QString& text ) const -{ - QLabel* label = new QLabel( text ); - label->setObjectName( "summaryItemTitle" ); - QFont fnt = font(); - fnt.setWeight( QFont::Light ); - fnt.setPointSize( CalamaresUtils::defaultFontSize() * 2 ); - label->setFont( fnt ); - label->setContentsMargins( 0, 0, 0, 0 ); - - return label; -} - -QLabel* -SummaryPage::createBodyLabel( const QString& text ) const -{ - QLabel* label = new QLabel; - label->setObjectName( "summaryItemBody" ); - label->setMargin( CalamaresUtils::defaultFontHeight() / 2 ); - QPalette pal( palette() ); - pal.setColor( WindowBackground, palette().window().color().lighter( 108 ) ); - label->setAutoFillBackground( true ); - label->setPalette( pal ); - label->setText( text ); - return label; -} diff --git a/src/modules/summary/SummaryPage.h b/src/modules/summary/SummaryPage.h index 6e5f25c05..718328c8f 100644 --- a/src/modules/summary/SummaryPage.h +++ b/src/modules/summary/SummaryPage.h @@ -61,9 +61,6 @@ private: QVBoxLayout* m_layout = nullptr; QWidget* m_contentWidget = nullptr; - QLabel* createTitleLabel( const QString& text ) const; - QLabel* createBodyLabel( const QString& text ) const; - QScrollArea* m_scrollArea; }; From 949f9e466f726f0c25950c8cab7f696e90676722 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 20 Jul 2021 16:24:09 +0200 Subject: [PATCH 24/87] [summary] createContentWidget is called exactly once, refactor --- src/modules/summary/SummaryPage.cpp | 15 ++++----------- src/modules/summary/SummaryPage.h | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/modules/summary/SummaryPage.cpp b/src/modules/summary/SummaryPage.cpp index 1c44a3063..8abfb8804 100644 --- a/src/modules/summary/SummaryPage.cpp +++ b/src/modules/summary/SummaryPage.cpp @@ -89,7 +89,10 @@ createBodyLabel( const QString& text, const QPalette& bodyPalette ) void SummaryPage::onActivate() { - createContentWidget(); + delete m_contentWidget; // It might have been created previously + m_contentWidget = new QWidget; + m_layout = new QVBoxLayout( m_contentWidget ); + CalamaresUtils::unmarginLayout( m_layout ); QFont titleFont = font(); titleFont.setWeight( QFont::Light ); @@ -184,16 +187,6 @@ SummaryPage::stepsForSummary( const Calamares::ViewStepList& allSteps ) const return steps; } - -void -SummaryPage::createContentWidget() -{ - delete m_contentWidget; - m_contentWidget = new QWidget; - m_layout = new QVBoxLayout( m_contentWidget ); - CalamaresUtils::unmarginLayout( m_layout ); -} - void SummaryPage::onLeave() { diff --git a/src/modules/summary/SummaryPage.h b/src/modules/summary/SummaryPage.h index 718328c8f..7d98cc711 100644 --- a/src/modules/summary/SummaryPage.h +++ b/src/modules/summary/SummaryPage.h @@ -54,7 +54,6 @@ public: private: Calamares::ViewStepList stepsForSummary( const Calamares::ViewStepList& allSteps ) const; - void createContentWidget(); const SummaryViewStep* m_thisViewStep; From b8583a1e59943a4269a38df6f7e033aedebd0a8a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 1 Aug 2021 22:07:51 +0200 Subject: [PATCH 25/87] [libcalamares] Expand the number of URLs to check for connectivity - introduce a list of URLs instead of just one - ping each of them, in turn, until one responds --- src/libcalamares/network/Manager.cpp | 40 ++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index 79db1a587..e5277d74e 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -27,7 +27,7 @@ namespace Network void RequestOptions::applyToRequest( QNetworkRequest* request ) const { -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) +#if QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) constexpr const auto RedirectPolicyAttribute = QNetworkRequest::FollowRedirectsAttribute; #else constexpr const auto RedirectPolicyAttribute = QNetworkRequest::RedirectPolicyAttribute; @@ -60,8 +60,9 @@ public slots: void cleanupNam(); public: - QUrl m_hasInternetUrl; - bool m_hasInternet; + QVector< QUrl > m_hasInternetUrls; + bool m_hasInternet = false; + int m_lastCheckedUrlIndex = -1; Private(); @@ -155,8 +156,33 @@ Manager::hasInternet() bool Manager::checkHasInternet() { + if ( d->m_hasInternetUrls.empty() ) + { + return false; + } + if ( d->m_lastCheckedUrlIndex < 0 ) + { + d->m_lastCheckedUrlIndex = 0; + } + int attempts = 0; + do + { + // Start by pinging the same one as last time + d->m_hasInternet = synchronousPing( d->m_hasInternetUrls.at( d->m_lastCheckedUrlIndex ) ); + // if it's not responding, **then** move on to the next one, + // and wrap around if needed + if ( !d->m_hasInternet ) + { + if ( ++( d->m_lastCheckedUrlIndex ) >= d->m_hasInternetUrls.size() ) + { + d->m_lastCheckedUrlIndex = 0; + } + } + // keep track of how often we've tried, because there's no point in + // going around more than once. + attempts++; + } while ( !d->m_hasInternet && ( attempts < d->m_hasInternetUrls.size() ) ); - d->m_hasInternet = synchronousPing( d->m_hasInternetUrl ); // For earlier Qt versions (< 5.15.0), set the accessibility flag to // NotAccessible if synchronous ping has failed, so that any module @@ -177,7 +203,11 @@ Manager::checkHasInternet() void Manager::setCheckHasInternetUrl( const QUrl& url ) { - d->m_hasInternetUrl = url; + if ( d->m_hasInternetUrls.empty() ) + { + d->m_lastCheckedUrlIndex = -1; + } + d->m_hasInternetUrls.append( url ); } /** @brief Does a request asynchronously, returns the (pending) reply From 81fe8b148834d236c78404e2e91539be4d689a70 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 1 Aug 2021 22:37:13 +0200 Subject: [PATCH 26/87] [libcalamares] Expand API for setting URLs to check --- src/libcalamares/network/Manager.cpp | 19 +++++++++++++++---- src/libcalamares/network/Manager.h | 7 +++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index e5277d74e..62ab60e60 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -203,10 +203,21 @@ Manager::checkHasInternet() void Manager::setCheckHasInternetUrl( const QUrl& url ) { - if ( d->m_hasInternetUrls.empty() ) - { - d->m_lastCheckedUrlIndex = -1; - } + d->m_lastCheckedUrlIndex = -1; + d->m_hasInternetUrls.clear(); + d->m_hasInternetUrls.append( url ); +} + +void +Manager::setCheckHasInternetUrl( const QVector< QUrl >& urls ) +{ + d->m_lastCheckedUrlIndex = -1; + d->m_hasInternetUrls = urls; +} + +void +Manager::addCheckHasInternetUrl( const QUrl& url ) +{ d->m_hasInternetUrls.append( url ); } diff --git a/src/libcalamares/network/Manager.h b/src/libcalamares/network/Manager.h index a038dceae..8bc3dded7 100644 --- a/src/libcalamares/network/Manager.h +++ b/src/libcalamares/network/Manager.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,12 @@ public: /// @brief Set the URL which is used for the general "is there internet" check. void setCheckHasInternetUrl( const QUrl& url ); + /// @brief Adds an (extra) URL to check + void addCheckHasInternetUrl( const QUrl& url ); + + /// @brief Set a collection of URLs used for the general "is there internet" check. + void setCheckHasInternetUrl( const QVector< QUrl >& urls ); + /** @brief Do a network request asynchronously. * * Returns a pointer to the reply-from-the-request. From 2f3062f4c298873700998f9304e0c2045f690e45 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 1 Aug 2021 23:49:33 +0200 Subject: [PATCH 27/87] [libcalamares] Fix typo in comment --- src/libcalamares/utils/Variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcalamares/utils/Variant.h b/src/libcalamares/utils/Variant.h index ab9e73f90..d0d893dc3 100644 --- a/src/libcalamares/utils/Variant.h +++ b/src/libcalamares/utils/Variant.h @@ -34,7 +34,7 @@ DLLEXPORT QString getString( const QVariantMap& map, const QString& key, const Q /** @brief 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 + * This is slightly more lenient than 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() ); From 1452b747402928edf4ddd84431429ade859ebe82 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 1 Aug 2021 23:52:27 +0200 Subject: [PATCH 28/87] [welcome] Load potentially a list of URLs to check --- .../welcome/checker/GeneralRequirements.cpp | 76 +++++++++++++------ src/modules/welcome/welcome.conf | 15 ++++ 2 files changed, 67 insertions(+), 24 deletions(-) diff --git a/src/modules/welcome/checker/GeneralRequirements.cpp b/src/modules/welcome/checker/GeneralRequirements.cpp index 07c352946..2528cbd9f 100644 --- a/src/modules/welcome/checker/GeneralRequirements.cpp +++ b/src/modules/welcome/checker/GeneralRequirements.cpp @@ -215,6 +215,57 @@ GeneralRequirements::checkRequirements() return checkEntries; } +/** @brief Loads the check-internel URLs + * + * There may be zero or one or more URLs specified; returns + * @c true if the configuration is incomplete or damaged in some way. + */ +static bool +getCheckInternetUrls( const QVariantMap& configurationMap ) +{ + const QString exampleUrl = QStringLiteral( "http://example.com" ); + + bool incomplete = false; + QStringList checkInternetSetting = CalamaresUtils::getStringList( configurationMap, "internetCheckUrl" ); + if ( !checkInternetSetting.isEmpty() ) + { + QVector< QUrl > urls; + for ( const auto& urlString : qAsConst( checkInternetSetting ) ) + { + QUrl url( urlString.trimmed() ); + if ( url.isValid() ) + { + urls.append( url ); + } + else + { + cWarning() << "GeneralRequirements entry 'internetCheckUrl' in welcome.conf contains invalid" + << urlString; + } + } + + if ( urls.empty() ) + { + cWarning() << "GeneralRequirements entry 'internetCheckUrl' contains no valid URLs," + << "reverting to default (http://example.com)."; + CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( QUrl( exampleUrl ) ); + incomplete = true; + } + else + { + CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( urls ); + } + } + else + { + cWarning() << "GeneralRequirements entry 'internetCheckUrl' is undefined in welcome.conf," + "reverting to default (http://example.com)."; + CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( QUrl( exampleUrl ) ); + incomplete = true; + } + return incomplete; +} + void GeneralRequirements::setConfigurationMap( const QVariantMap& configurationMap ) @@ -302,30 +353,7 @@ GeneralRequirements::setConfigurationMap( const QVariantMap& configurationMap ) incompleteConfiguration = true; } - QUrl checkInternetUrl; - QString checkInternetSetting = CalamaresUtils::getString( configurationMap, "internetCheckUrl" ); - if ( !checkInternetSetting.isEmpty() ) - { - checkInternetUrl = QUrl( checkInternetSetting.trimmed() ); - if ( !checkInternetUrl.isValid() ) - { - cWarning() << "GeneralRequirements entry 'internetCheckUrl' is invalid in welcome.conf" - << checkInternetSetting << "reverting to default (http://example.com)."; - checkInternetUrl = QUrl( "http://example.com" ); - incompleteConfiguration = true; - } - } - else - { - cWarning() << "GeneralRequirements entry 'internetCheckUrl' is undefined in welcome.conf," - "reverting to default (http://example.com)."; - checkInternetUrl = "http://example.com"; - incompleteConfiguration = true; - } - if ( checkInternetUrl.isValid() ) - { - CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( checkInternetUrl ); - } + incompleteConfiguration |= getCheckInternetUrls( configurationMap ); if ( incompleteConfiguration ) { diff --git a/src/modules/welcome/welcome.conf b/src/modules/welcome/welcome.conf index b3da8d366..6e11817bf 100644 --- a/src/modules/welcome/welcome.conf +++ b/src/modules/welcome/welcome.conf @@ -43,6 +43,21 @@ requirements: # # The URL is only used if "internet" is in the *check* list below. internetCheckUrl: http://example.com + # + # This may be a single URL, or a list or URLs, in which case the + # URLs will be checked one-by-one; if any of them returns data, + # internet is assumed to be OK. This can be used to check via + # a number of places, where some domains may be down or blocked. + # + # To use a list of URLs, just use YAML list syntax (e.g. + # + # internetCheckUrl: + # - http://www.kde.org + # - http://www.freebsd.org + # + # or short-form + # + # internetCheckUrl: [ http://www.kde.org, http://www.freebsd.org ] # List conditions to check. Each listed condition will be # probed in some way, and yields true or false according to From e294221a2d6628bfac997d88affa99c287b3b5ed Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 13:08:54 +0200 Subject: [PATCH 29/87] [partition] Rename test executables - all partition tests are now named partitiontest --- src/modules/partition/tests/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index 8edef484c..c8c6f7fd4 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -15,7 +15,7 @@ include_directories( ) calamares_add_test( - partitionjobtests + partitionjobtest SOURCES PartitionJobTests.cpp ${PartitionModule_SOURCE_DIR}/core/KPMHelpers.cpp @@ -31,7 +31,7 @@ calamares_add_test( ) calamares_add_test( - clearmountsjobtests + partitionclearmountsjobtest SOURCES ${PartitionModule_SOURCE_DIR}/jobs/ClearMountsJob.cpp ClearMountsJobTests.cpp @@ -42,7 +42,7 @@ calamares_add_test( calamares_add_test( - createlayoutstests + partitioncreatelayoutstest SOURCES ${PartitionModule_SOURCE_DIR}/core/KPMHelpers.cpp ${PartitionModule_SOURCE_DIR}/core/PartitionInfo.cpp @@ -59,7 +59,7 @@ calamares_add_test( ) calamares_add_test( - automounttests + partitionautomounttest SOURCES ${PartitionModule_SOURCE_DIR}/jobs/AutoMountManagementJob.cpp AutoMountTests.cpp From 22ba21f9375aa33acff3fe83ffd845ff74443605 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 13:16:45 +0200 Subject: [PATCH 30/87] [partition] Stub of a test of scandevice --- src/modules/partition/tests/CMakeLists.txt | 11 ++-- src/modules/partition/tests/DevicesTests.cpp | 56 ++++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/modules/partition/tests/DevicesTests.cpp diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index c8c6f7fd4..6c7abc183 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -52,9 +52,6 @@ calamares_add_test( CreateLayoutsTests.cpp LIBRARIES kpmcore - calamares - calamaresui - Qt5::Gui DEFINITIONS ${_partition_defs} ) @@ -63,6 +60,10 @@ calamares_add_test( SOURCES ${PartitionModule_SOURCE_DIR}/jobs/AutoMountManagementJob.cpp AutoMountTests.cpp - LIBRARIES - calamares +) + +calamares_add_test( + partitiondevicestest + SOURCES + DevicesTests.cpp ) diff --git a/src/modules/partition/tests/DevicesTests.cpp b/src/modules/partition/tests/DevicesTests.cpp new file mode 100644 index 000000000..82311b288 --- /dev/null +++ b/src/modules/partition/tests/DevicesTests.cpp @@ -0,0 +1,56 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "core/DeviceList.h" + +#include "utils/Logger.h" + +#include +#include + +class DevicesTests : public QObject +{ + Q_OBJECT + +public: + DevicesTests(); + +private Q_SLOTS: + void testKPMScanDevices(); + void testPartUtilScanDevices(); +}; + +DevicesTests::DevicesTests() {} + +void +DevicesTests::testKPMScanDevices() +{ + Logger::setupLogLevel( Logger::LOGVERBOSE ); + +#if defined( WITH_KPMCORE4API ) + cDebug() << "Getting devices via KPMCore"; + CoreBackend* backend = CoreBackendManager::self()->backend(); + DeviceList devices = backend->scanDevices( /* not includeReadOnly, not includeLoopback */ ScanFlag( 0 ) ); + cDebug() << Logger::SubEntry << "Done getting devices."; +#else + cWarning() << "Test skipped; use KPMCore4"; +#endif +} + +void +DevicesTests::testPartUtilScanDevices() +{ + Logger::setupLogLevel( Logger::LOGVERBOSE ); + + cDebug() << "Getting devices via PartUtils"; + auto l = PartUtils::getDevices(); + cDebug() << Logger::SubEntry << "Done getting devices."; + + QVERIFY( l.count() > 0 ); +} From 854eb845d2d2c8226019cfde1968994d29cc7d20 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 5 Aug 2021 15:49:29 +0200 Subject: [PATCH 31/87] [partition] Fix build of layoutstest This test needs UI because the devicemodel uses GUI parts. --- src/modules/partition/tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index 6c7abc183..bb0b59af4 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -52,6 +52,7 @@ calamares_add_test( CreateLayoutsTests.cpp LIBRARIES kpmcore + Calamares::calamaresui DEFINITIONS ${_partition_defs} ) From 74a3a2da78ec59996fa313b894c40f5fec2d07ea Mon Sep 17 00:00:00 2001 From: Artem Grinev Date: Thu, 12 Aug 2021 02:13:16 +0400 Subject: [PATCH 32/87] [partition] Fix Delete button for extended Extended partition can't be removed when contains children. This commit adds missing check. --- src/modules/partition/gui/PartitionPage.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/modules/partition/gui/PartitionPage.cpp b/src/modules/partition/gui/PartitionPage.cpp index f5a3b0f43..3e2bb4bd1 100644 --- a/src/modules/partition/gui/PartitionPage.cpp +++ b/src/modules/partition/gui/PartitionPage.cpp @@ -139,6 +139,11 @@ PartitionPage::updateButtons() bool isFree = CalamaresUtils::Partition::isPartitionFreeSpace( partition ); bool isExtended = partition->roles().has( PartitionRole::Extended ); + bool hasChildren = isExtended + && ( partition->children().length() > 1 + || ( partition->children().length() == 1 + && !CalamaresUtils::Partition::isPartitionFreeSpace( partition->children().at( 0 ) ) ) ); + bool isInVG = m_core->isInVG( partition ); create = isFree; @@ -151,7 +156,7 @@ PartitionPage::updateButtons() // order. // TODO: See if LVM PVs can be edited in Calamares edit = !isFree && !isExtended; - del = !isFree && !isInVG; + del = !isFree && !isInVG && !hasChildren; } if ( m_ui->deviceComboBox->currentIndex() >= 0 ) From 8913317a44e0a81a9a208ae71b52268005ec7a9a Mon Sep 17 00:00:00 2001 From: demmm Date: Mon, 16 Aug 2021 14:06:13 +0200 Subject: [PATCH 33/87] [networkcfg] add setting the correct target user in copied file see https://github.com/calamares/calamares/issues/1753 --- src/modules/networkcfg/main.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/modules/networkcfg/main.py b/src/modules/networkcfg/main.py index 40d153031..cc6c394fe 100644 --- a/src/modules/networkcfg/main.py +++ b/src/modules/networkcfg/main.py @@ -7,6 +7,7 @@ # SPDX-FileCopyrightText: 2014 Teo Mrnjavac # SPDX-FileCopyrightText: 2017 Alf Gaida # SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-FileCopyrightText: 2021 Anke boersma # SPDX-License-Identifier: GPL-3.0-or-later # # Calamares is Free Software: see the License-Identifier above. @@ -14,6 +15,7 @@ import os import shutil +import getpass import libcalamares @@ -33,6 +35,8 @@ def run(): Setup network configuration """ root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + user = libcalamares.globalstorage.value("username") + live_user = getpass.getuser() if root_mount_point is None: libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) @@ -60,6 +64,16 @@ def run(): try: shutil.copy(source_network, target_network, follow_symlinks=False) + if live_user in open(target_network).read(): + text = [] + with open(target_network, "r") as network_conf: + text = network_conf.readlines() + with open(target_network, "w") as network_conf: + for line in text: + if 'permissions=user:{}:;'.format(live_user) in line: + line = 'permissions=user:{}:;\n'.format(user) + network_conf.write(line) + network_conf.close() except FileNotFoundError: libcalamares.utils.debug( "Can't copy network configuration files in " From 805fed559c904e6074095276d93f25f215ca3d55 Mon Sep 17 00:00:00 2001 From: demmm Date: Mon, 16 Aug 2021 20:17:07 +0200 Subject: [PATCH 34/87] [networkcfg] use os.getlogin() cala running as root returns root for live_user otherwise --- src/modules/networkcfg/main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modules/networkcfg/main.py b/src/modules/networkcfg/main.py index cc6c394fe..35bf67d63 100644 --- a/src/modules/networkcfg/main.py +++ b/src/modules/networkcfg/main.py @@ -15,7 +15,6 @@ import os import shutil -import getpass import libcalamares @@ -36,7 +35,7 @@ def run(): """ root_mount_point = libcalamares.globalstorage.value("rootMountPoint") user = libcalamares.globalstorage.value("username") - live_user = getpass.getuser() + live_user = os.getlogin() if root_mount_point is None: libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) From b9f22a75260031a2e60eda28d6575b23908fc801 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 23 Aug 2021 17:37:08 +0200 Subject: [PATCH 35/87] Changes: mention the networkcfg improvement --- CHANGES | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 96282452d..4b8fcfd55 100644 --- a/CHANGES +++ b/CHANGES @@ -10,13 +10,15 @@ website will have to do for older versions. # 3.2.42 (unreleased) # This release contains contributions from (alphabetically by first name): - - No external contributors yet + - Anke Boersma ## Core ## - No core changes yet ## Modules ## - - No module changes yet + - *networkcfg* now translates the "live user" on an ISO to the regular + user on the installed system, so that network configuration changes + made in the live system are automatically used after installation. # 3.2.41.1 (2021-08-05) # From c79fc2e6d91af9b39a500f3a8749182776887a43 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 10:00:42 +0200 Subject: [PATCH 36/87] [libcalamares] Add urls only if valid, add tests to check that --- src/libcalamares/network/Manager.cpp | 14 ++++++- src/libcalamares/network/Tests.cpp | 55 ++++++++++++++++++++++++++++ src/libcalamares/network/Tests.h | 3 ++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index 62ab60e60..0e651f45c 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -20,6 +20,8 @@ #include #include +#include + namespace CalamaresUtils { namespace Network @@ -205,7 +207,10 @@ Manager::setCheckHasInternetUrl( const QUrl& url ) { d->m_lastCheckedUrlIndex = -1; d->m_hasInternetUrls.clear(); - d->m_hasInternetUrls.append( url ); + if ( url.isValid() ) + { + d->m_hasInternetUrls.append( url ); + } } void @@ -213,12 +218,17 @@ Manager::setCheckHasInternetUrl( const QVector< QUrl >& urls ) { d->m_lastCheckedUrlIndex = -1; d->m_hasInternetUrls = urls; + std::remove_if( + d->m_hasInternetUrls.begin(), d->m_hasInternetUrls.end(), []( const QUrl& u ) { return u.isValid(); } ); } void Manager::addCheckHasInternetUrl( const QUrl& url ) { - d->m_hasInternetUrls.append( url ); + if ( url.isValid() ) + { + d->m_hasInternetUrls.append( url ); + } } /** @brief Does a request asynchronously, returns the (pending) reply diff --git a/src/libcalamares/network/Tests.cpp b/src/libcalamares/network/Tests.cpp index d42a74115..d44f03781 100644 --- a/src/libcalamares/network/Tests.cpp +++ b/src/libcalamares/network/Tests.cpp @@ -60,3 +60,58 @@ NetworkTests::testPing() QVERIFY( canPing_www_kde_org ); } } + +void +NetworkTests::testCheckUrl() +{ + using namespace CalamaresUtils::Network; + Logger::setupLogLevel( Logger::LOGVERBOSE ); + auto& nam = Manager::instance(); + + { + QUrl u( "http://example.com" ); + QVERIFY( u.isValid() ); + nam.setCheckHasInternetUrl( u ); + QVERIFY( nam.checkHasInternet() ); + } + { + QUrl u( "http://nonexistent.example.com" ); + QVERIFY( u.isValid() ); + nam.setCheckHasInternetUrl( u ); + QVERIFY( !nam.checkHasInternet() ); + } + { + QUrl u; + QVERIFY( !u.isValid() ); + nam.setCheckHasInternetUrl( u ); + QVERIFY( !nam.checkHasInternet() ); + } +} + +void +NetworkTests::testCheckMultiUrl() +{ + using namespace CalamaresUtils::Network; + Logger::setupLogLevel( Logger::LOGVERBOSE ); + auto& nam = Manager::instance(); + + { + QUrl u0( "http://example.com" ); + QUrl u1( "https://kde.org" ); + QVERIFY( u0.isValid() ); + QVERIFY( u1.isValid() ); + nam.setCheckHasInternetUrl( { u0, u1 } ); + QVERIFY( nam.checkHasInternet() ); + } + { + QUrl u0( "http://nonexistent.example.com" ); + QUrl u1( "http://bogus.example.com" ); + QVERIFY( u0.isValid() ); + QVERIFY( u1.isValid() ); + nam.setCheckHasInternetUrl( { u0, u1 } ); + QVERIFY( !nam.checkHasInternet() ); + QVERIFY( !nam.checkHasInternet() ); + nam.addCheckHasInternetUrl( QUrl( "http://example.com" ) ); + QVERIFY( nam.checkHasInternet() ); + } +} diff --git a/src/libcalamares/network/Tests.h b/src/libcalamares/network/Tests.h index 6000e227a..d72da574a 100644 --- a/src/libcalamares/network/Tests.h +++ b/src/libcalamares/network/Tests.h @@ -24,6 +24,9 @@ private Q_SLOTS: void testInstance(); void testPing(); + + void testCheckUrl(); + void testCheckMultiUrl(); }; #endif From f1a47a9f0a16e7ba9a9e5b2e09a7906876b5e8f8 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 10:24:11 +0200 Subject: [PATCH 37/87] [welcome] Add (stub) test for the Config object This crashes because there's no translations object yet, but that is an internal issue. --- src/modules/welcome/CMakeLists.txt | 10 +++ src/modules/welcome/Tests.cpp | 61 +++++++++++++++++++ .../welcome/tests/1a-checkinternet.conf | 3 + 3 files changed, 74 insertions(+) create mode 100644 src/modules/welcome/Tests.cpp create mode 100644 src/modules/welcome/tests/1a-checkinternet.conf diff --git a/src/modules/welcome/CMakeLists.txt b/src/modules/welcome/CMakeLists.txt index 924062652..4895d4b80 100644 --- a/src/modules/welcome/CMakeLists.txt +++ b/src/modules/welcome/CMakeLists.txt @@ -42,3 +42,13 @@ calamares_add_plugin( welcome Qt5::Network SHARED_LIB ) + +calamares_add_test( + welcometest + SOURCES + Config.cpp + Tests.cpp + LIBRARIES + Qt5::Widgets + Calamares::calamaresui +) diff --git a/src/modules/welcome/Tests.cpp b/src/modules/welcome/Tests.cpp new file mode 100644 index 000000000..366de416f --- /dev/null +++ b/src/modules/welcome/Tests.cpp @@ -0,0 +1,61 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "Config.h" + +#include "utils/Logger.h" +#include "utils/Yaml.h" + +#include + +class WelcomeTests : public QObject +{ + Q_OBJECT +public: + WelcomeTests(); + ~WelcomeTests() override {} + +private Q_SLOTS: + void initTestCase(); + + void testOneUrl(); +}; + +WelcomeTests::WelcomeTests() {} + +void +WelcomeTests::initTestCase() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); + cDebug() << "Welcome test started."; +} + +void +WelcomeTests::testOneUrl() +{ + Config c; + + // BUILD_AS_TEST is the source-directory path + QString filename = QStringLiteral( "1a-checkinternet.conf" ); + QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) ); + QVERIFY( fi.exists() ); + + bool ok = false; + const auto map = CalamaresUtils::loadYaml( fi, &ok ); + QVERIFY( ok ); + QVERIFY( map.count() > 0 ); + QVERIFY( map.contains( "requirements" ) ); +} + + +QTEST_GUILESS_MAIN( WelcomeTests ) + +#include "utils/moc-warnings.h" + +#include "Tests.moc" diff --git a/src/modules/welcome/tests/1a-checkinternet.conf b/src/modules/welcome/tests/1a-checkinternet.conf new file mode 100644 index 000000000..d2140f201 --- /dev/null +++ b/src/modules/welcome/tests/1a-checkinternet.conf @@ -0,0 +1,3 @@ +--- +requirements: + internetCheckUrl: http://example.com From 38c65e80f3c61860fa7ba0b102d6012dd4ccc40a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 11:57:37 +0200 Subject: [PATCH 38/87] [libcalamaresui] Warn when asking for nonexistent Branding instance --- src/libcalamaresui/Branding.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcalamaresui/Branding.cpp b/src/libcalamaresui/Branding.cpp index b9445ba83..5894c723d 100644 --- a/src/libcalamaresui/Branding.cpp +++ b/src/libcalamaresui/Branding.cpp @@ -49,6 +49,10 @@ Branding* Branding::s_instance = nullptr; Branding* Branding::instance() { + if ( !s_instance ) + { + cWarning() << "No Branding instance created yet."; + } return s_instance; } From e9a98f35ad1b61e0d60f3c28f110fe482f99d102 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 11:58:24 +0200 Subject: [PATCH 39/87] [welcome] Avoid crash when no Branding available - don't install translators twice -- do it in setLocaleIndex only - avoid crash if the branding instance is nullptr --- src/modules/welcome/Config.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/welcome/Config.cpp b/src/modules/welcome/Config.cpp index bc489d186..52cd4c88d 100644 --- a/src/modules/welcome/Config.cpp +++ b/src/modules/welcome/Config.cpp @@ -152,8 +152,6 @@ Config::initLanguages() { QString name = m_languages->locale( matchedLocaleIndex ).name(); cDebug() << Logger::SubEntry << "Matched with index" << matchedLocaleIndex << name; - - CalamaresUtils::installTranslator( name, Calamares::Branding::instance()->translationsDirectory() ); setLocaleIndex( matchedLocaleIndex ); } else @@ -192,7 +190,8 @@ Config::setLocaleIndex( int index ) cDebug() << "Index" << index << "Selected locale" << selectedLocale; QLocale::setDefault( selectedLocale ); - CalamaresUtils::installTranslator( selectedLocale, Calamares::Branding::instance()->translationsDirectory() ); + const auto* branding = Calamares::Branding::instance(); + CalamaresUtils::installTranslator( selectedLocale, branding ? branding->translationsDirectory() : QString() ); if ( Calamares::JobQueue::instance() && Calamares::JobQueue::instance()->globalStorage() ) { CalamaresUtils::Locale::insertGS( *Calamares::JobQueue::instance()->globalStorage(), From 14c26d01afb881f0114ab85e8532cf1c8fadb070 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 12:30:44 +0200 Subject: [PATCH 40/87] [libcalamares] Warnings for nullptr Settings --- src/libcalamares/Settings.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libcalamares/Settings.cpp b/src/libcalamares/Settings.cpp index 9453075b6..2620b8563 100644 --- a/src/libcalamares/Settings.cpp +++ b/src/libcalamares/Settings.cpp @@ -104,6 +104,10 @@ Settings* Settings::s_instance = nullptr; Settings* Settings::instance() { + if ( !s_instance ) + { + cWarning() << "Getting nullptr Settings instance."; + } return s_instance; } @@ -238,6 +242,9 @@ Settings::Settings( bool debugMode ) , m_disableCancel( false ) , m_disableCancelDuringExec( false ) { + cWarning() << "Using bogus Calamares settings in" + << ( debugMode ? QStringLiteral( "debug" ) : QStringLiteral( "regular" ) ) << "mode"; + s_instance = this; } Settings::Settings( const QString& settingsFilePath, bool debugMode ) From 1e05e7996b778a6d9c7c4a5192008606ad2ea0ef Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 12:32:48 +0200 Subject: [PATCH 41/87] [libcalamares] Avoid cError + SubEntry The combination of Error and SubEntry loses the indentation. --- src/libcalamares/utils/CalamaresUtilsSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcalamares/utils/CalamaresUtilsSystem.cpp b/src/libcalamares/utils/CalamaresUtilsSystem.cpp index 29f743743..df578a862 100644 --- a/src/libcalamares/utils/CalamaresUtilsSystem.cpp +++ b/src/libcalamares/utils/CalamaresUtilsSystem.cpp @@ -102,7 +102,7 @@ System::instance() if ( !s_instance ) { cError() << "No Calamares system-object has been created."; - cError() << Logger::SubEntry << "using a bogus instance instead."; + cDebug() << Logger::SubEntry << "using a bogus instance instead."; return new System( true, nullptr ); } return s_instance; From e0ee2d9514bba73bf384b6794cfc4f4b5a0dd000 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 12:50:03 +0200 Subject: [PATCH 42/87] [welcome] Handle nullptrs nicely in Config - Branding, Settings, and ModuleManager may all be nullptr, in which case the corresponding code shouldn't call methods of those instances -- this is demonstrated by just creating a Config object --- src/modules/welcome/Config.cpp | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/modules/welcome/Config.cpp b/src/modules/welcome/Config.cpp index 52cd4c88d..989475153 100644 --- a/src/modules/welcome/Config.cpp +++ b/src/modules/welcome/Config.cpp @@ -40,14 +40,16 @@ Config::retranslate() { cWarning() << "Retranslated to" << QLocale().name(); - m_genericWelcomeMessage = genericWelcomeMessage().arg( Calamares::Branding::instance()->versionedName() ); + const auto* branding = Calamares::Branding::instance(); + const auto* settings = Calamares::Settings::instance(); + m_genericWelcomeMessage = genericWelcomeMessage().arg( branding ? branding->versionedName() : QString() ); emit genericWelcomeMessageChanged( m_genericWelcomeMessage ); const auto* r = requirementsModel(); - if ( !r->satisfiedRequirements() ) + if ( r && !r->satisfiedRequirements() ) { QString message; - const bool setup = Calamares::Settings::instance()->isSetupMode(); + const bool setup = settings ? settings->isSetupMode() : false; if ( !r->satisfiedMandatory() ) { @@ -72,13 +74,13 @@ Config::retranslate() "might be disabled." ); } - m_warningMessage = message.arg( Calamares::Branding::instance()->shortVersionedName() ); + m_warningMessage = message.arg( branding ? branding->shortVersionedName() : QString() ); } else { m_warningMessage = tr( "This program will ask you some questions and " "set up %2 on your computer." ) - .arg( Calamares::Branding::instance()->productName() ); + .arg( branding ? branding->productName() : QString() ); } emit warningMessageChanged( m_warningMessage ); @@ -93,7 +95,8 @@ Config::languagesModel() const Calamares::RequirementsModel* Config::requirementsModel() const { - return Calamares::ModuleManager::instance()->requirementsModel(); + auto* manager = Calamares::ModuleManager::instance(); + return manager ? manager->requirementsModel() : nullptr; } QAbstractItemModel* @@ -241,17 +244,19 @@ Config::genericWelcomeMessage() const { QString message; - if ( Calamares::Settings::instance()->isSetupMode() ) + const auto* settings = Calamares::Settings::instance(); + const auto* branding = Calamares::Branding::instance(); + const bool welcomeStyle = branding ? branding->welcomeStyleCalamares() : true; + + if ( settings ? settings->isSetupMode() : false ) { - message = Calamares::Branding::instance()->welcomeStyleCalamares() - ? tr( "

    Welcome to the Calamares setup program for %1

    " ) - : tr( "

    Welcome to %1 setup

    " ); + message = welcomeStyle ? tr( "

    Welcome to the Calamares setup program for %1

    " ) + : tr( "

    Welcome to %1 setup

    " ); } else { - message = Calamares::Branding::instance()->welcomeStyleCalamares() - ? tr( "

    Welcome to the Calamares installer for %1

    " ) - : tr( "

    Welcome to the %1 installer

    " ); + message = welcomeStyle ? tr( "

    Welcome to the Calamares installer for %1

    " ) + : tr( "

    Welcome to the %1 installer

    " ); } return message; From d5e6e1075d9cdf5f32e9b7e746f31117d8986ba7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 24 Aug 2021 13:37:02 +0200 Subject: [PATCH 43/87] [welcome] Expand stub tests to check that crashes are gone --- src/modules/welcome/Tests.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/modules/welcome/Tests.cpp b/src/modules/welcome/Tests.cpp index 366de416f..eed7dae87 100644 --- a/src/modules/welcome/Tests.cpp +++ b/src/modules/welcome/Tests.cpp @@ -9,6 +9,9 @@ #include "Config.h" +#include "Branding.h" +#include "Settings.h" +#include "utils/CalamaresUtilsSystem.h" #include "utils/Logger.h" #include "utils/Yaml.h" @@ -34,6 +37,17 @@ WelcomeTests::initTestCase() { Logger::setupLogLevel( Logger::LOGDEBUG ); cDebug() << "Welcome test started."; + + // Ensure we have a system object, expect it to be a "bogus" one + CalamaresUtils::System* system = CalamaresUtils::System::instance(); + QVERIFY( system ); + cDebug() << Logger::SubEntry << "System @" << Logger::Pointer( system ); + + const auto* settings = Calamares::Settings::instance(); + if ( !settings ) + { + (void)new Calamares::Settings( true ); + } } void From 7bfb769288a96cd3093e9952b3ce0e353212d7a2 Mon Sep 17 00:00:00 2001 From: demmm Date: Thu, 26 Aug 2021 12:05:38 +0200 Subject: [PATCH 44/87] [localeq] redo i18n.qml set colors as vars, mostly kirigami colors, to make it work for boths dark & light themes highlights now full width and on hover currentindex still at -1, not implemented in cofig.cpp/h, possible to use js, or is https://github.com/calamares/calamares/blob/calamares/src/modules/locale/LCLocaleDialog.cpp#L43 accessible to QML? --- src/modules/localeq/i18n.qml | 103 +++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/src/modules/localeq/i18n.qml b/src/modules/localeq/i18n.qml index 63cad8bf5..3ceae2ab8 100644 --- a/src/modules/localeq/i18n.qml +++ b/src/modules/localeq/i18n.qml @@ -1,6 +1,6 @@ /* === This file is part of Calamares - === * - * SPDX-FileCopyrightText: 2020 Anke Boersma + * SPDX-FileCopyrightText: 2020 - 2021 Anke Boersma * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. @@ -16,19 +16,23 @@ import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami Item { + readonly property color backgroundColor: Kirigami.Theme.backgroundColor //"#F5F5F5" + readonly property color headerBackgroundColor: Kirigami.Theme.alternateBackgroundColor //"#d3d3d3" + readonly property color backgroundLighterColor: "#ffffff" + readonly property color highlightColor: Kirigami.Theme.highlightColor //"#3498DB" + readonly property color textColor: Kirigami.Theme.textColor + readonly property color highlightedTextColor: Kirigami.Theme.highlightedTextColor + width: parent.width height: parent.height focus: true - MouseArea { - anchors.fill: parent - } Rectangle { id: textArea x: 28 y: 14 anchors.fill: parent - Kirigami.Theme.backgroundColor: Kirigami.Theme.backgroundColor + color: backgroundColor Column { id: languages @@ -38,7 +42,7 @@ Item { Rectangle { width: 250 height: 140 - color: "#d3d3d3" + color: headerBackgroundColor Text { anchors.top: parent.top width: 240 @@ -57,30 +61,43 @@ Item { id: scroll1 anchors.fill: parent contentHeight: 800 - clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ListView { id: list1 focus: true + clip: true + width: parent.width model: config.supportedLocales - currentIndex: -1 - highlight: Rectangle { - color: Kirigami.Theme.highlightColor - } - delegate: Text { - text: modelData + currentIndex: -1 //model.currentLanguageCodeIndex + delegate: ItemDelegate { - MouseArea { - hoverEnabled: true - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onEntered: { - color: "#0000ff" + hoverEnabled: true + width: parent.width + implicitHeight: 18 + highlighted: ListView.isCurrentItem + Label { + Layout.fillHeight: true + Layout.fillWidth: true + width: parent.width + height: 18 + color: highlighted ? highlightedTextColor : textColor + text: modelData + background: Rectangle { + + color: highlighted || hovered ? highlightColor : backgroundLighterColor + opacity: highlighted || hovered ? 0.5 : 0.9 } - onClicked: { - list1.currentIndex = index + + MouseArea { + hoverEnabled: true + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + list1.currentIndex = index + } } } } @@ -98,7 +115,7 @@ Item { Rectangle { width: 250 height: 140 - color: "#d3d3d3" + color: headerBackgroundColor Text { anchors.top: parent.top width: 240 @@ -117,28 +134,42 @@ Item { id: scroll2 anchors.fill: parent contentHeight: 800 - clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ListView { id: list2 - width: 180; height: 200 focus: true + clip: true model: config.supportedLocales - currentIndex: -1 - highlight: Rectangle { - color: Kirigami.Theme.highlightColor - } - delegate: Text { - text: modelData + currentIndex: -1 //model.currentLCCodeIndex + delegate: ItemDelegate { - MouseArea { - hoverEnabled: true - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - list2.currentIndex = index + hoverEnabled: true + width: parent.width + implicitHeight: 18 + highlighted: ListView.isCurrentItem + Label { + Layout.fillHeight: true + Layout.fillWidth: true + width: parent.width + height: 18 + color: highlighted ? highlightedTextColor : textColor + text: modelData + background: Rectangle { + + color: highlighted || hovered ? highlightColor : backgroundLighterColor + opacity: highlighted || hovered ? 0.5 : 0.9 + } + + MouseArea { + hoverEnabled: true + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + list2.currentIndex = index + } } } } From 653359d815ce4113ff384848e3c0dd5b430bb1e7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 12:57:40 +0200 Subject: [PATCH 45/87] [libcalamares] Fix up multiple URLs for checkinternet - was filtering out the wrong URLs - was not actually removing the invalid URLs - extend API to make it possible to count / confirm the settings - extend tests to demonstrate that API and the issues --- src/libcalamares/network/Manager.cpp | 14 ++++++++++++-- src/libcalamares/network/Manager.h | 4 ++++ src/libcalamares/network/Tests.cpp | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index 0e651f45c..5125f592e 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -218,8 +218,12 @@ Manager::setCheckHasInternetUrl( const QVector< QUrl >& urls ) { d->m_lastCheckedUrlIndex = -1; d->m_hasInternetUrls = urls; - std::remove_if( - d->m_hasInternetUrls.begin(), d->m_hasInternetUrls.end(), []( const QUrl& u ) { return u.isValid(); } ); + auto it = std::remove_if( + d->m_hasInternetUrls.begin(), d->m_hasInternetUrls.end(), []( const QUrl& u ) { return !u.isValid(); } ); + if ( it != d->m_hasInternetUrls.end() ) + { + d->m_hasInternetUrls.erase( it ); + } } void @@ -231,6 +235,12 @@ Manager::addCheckHasInternetUrl( const QUrl& url ) } } +QVector< QUrl > +Manager::getCheckInternetUrls() const +{ + return d->m_hasInternetUrls; +} + /** @brief Does a request asynchronously, returns the (pending) reply * * The extra options for the request are taken from @p options, diff --git a/src/libcalamares/network/Manager.h b/src/libcalamares/network/Manager.h index 8bc3dded7..6a906c883 100644 --- a/src/libcalamares/network/Manager.h +++ b/src/libcalamares/network/Manager.h @@ -90,6 +90,7 @@ class DLLEXPORT Manager : public QObject { Q_OBJECT Q_PROPERTY( bool hasInternet READ hasInternet NOTIFY hasInternetChanged FINAL ) + Q_PROPERTY( QVector< QUrl > checkInternetUrls READ getCheckInternetUrls WRITE setCheckHasInternetUrl ) Manager(); @@ -129,6 +130,9 @@ public: /// @brief Set a collection of URLs used for the general "is there internet" check. void setCheckHasInternetUrl( const QVector< QUrl >& urls ); + /// @brief What URLs are used to check for internet connectivity? + QVector< QUrl > getCheckInternetUrls() const; + /** @brief Do a network request asynchronously. * * Returns a pointer to the reply-from-the-request. diff --git a/src/libcalamares/network/Tests.cpp b/src/libcalamares/network/Tests.cpp index d44f03781..a5bc52497 100644 --- a/src/libcalamares/network/Tests.cpp +++ b/src/libcalamares/network/Tests.cpp @@ -30,6 +30,7 @@ NetworkTests::testInstance() { auto& nam = CalamaresUtils::Network::Manager::instance(); QVERIFY( !nam.hasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 0 ); } void @@ -73,18 +74,21 @@ NetworkTests::testCheckUrl() QVERIFY( u.isValid() ); nam.setCheckHasInternetUrl( u ); QVERIFY( nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 1 ); // Valid URL } { QUrl u( "http://nonexistent.example.com" ); QVERIFY( u.isValid() ); nam.setCheckHasInternetUrl( u ); QVERIFY( !nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 1 ); // Valid URL even if it doesn't resolve } { QUrl u; QVERIFY( !u.isValid() ); nam.setCheckHasInternetUrl( u ); QVERIFY( !nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 0 ); // Invalid URL tried } } @@ -102,6 +106,7 @@ NetworkTests::testCheckMultiUrl() QVERIFY( u1.isValid() ); nam.setCheckHasInternetUrl( { u0, u1 } ); QVERIFY( nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 2 ); } { QUrl u0( "http://nonexistent.example.com" ); @@ -111,7 +116,22 @@ NetworkTests::testCheckMultiUrl() nam.setCheckHasInternetUrl( { u0, u1 } ); QVERIFY( !nam.checkHasInternet() ); QVERIFY( !nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 2 ); // Both are valid URLs nam.addCheckHasInternetUrl( QUrl( "http://example.com" ) ); QVERIFY( nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 3 ); + } + { + QUrl u0( "http://nonexistent.example.com" ); + QUrl u1; + QVERIFY( u0.isValid() ); + QVERIFY( !u1.isValid() ); + nam.setCheckHasInternetUrl( { u0, u1 } ); + QVERIFY( !nam.checkHasInternet() ); + QVERIFY( !nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 1 ); // Only valid URL added + nam.addCheckHasInternetUrl( QUrl( "http://example.com" ) ); + QVERIFY( nam.checkHasInternet() ); + QCOMPARE( nam.getCheckInternetUrls().count(), 2 ); } } From 05388814476f6e48bb70bcb82aeef6da7cb46ab7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 14:32:57 +0200 Subject: [PATCH 46/87] [libcalamares] Handle multiple invalid URLs at once - expand tests with example where more than one URL is invalid - fix the call to the wrong overload of QVector::erase() --- src/libcalamares/network/Manager.cpp | 2 +- src/libcalamares/network/Tests.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index 5125f592e..cceff477e 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -222,7 +222,7 @@ Manager::setCheckHasInternetUrl( const QVector< QUrl >& urls ) d->m_hasInternetUrls.begin(), d->m_hasInternetUrls.end(), []( const QUrl& u ) { return !u.isValid(); } ); if ( it != d->m_hasInternetUrls.end() ) { - d->m_hasInternetUrls.erase( it ); + d->m_hasInternetUrls.erase( it, d->m_hasInternetUrls.end() ); } } diff --git a/src/libcalamares/network/Tests.cpp b/src/libcalamares/network/Tests.cpp index a5bc52497..e5bd34c23 100644 --- a/src/libcalamares/network/Tests.cpp +++ b/src/libcalamares/network/Tests.cpp @@ -134,4 +134,14 @@ NetworkTests::testCheckMultiUrl() QVERIFY( nam.checkHasInternet() ); QCOMPARE( nam.getCheckInternetUrls().count(), 2 ); } + { + QUrl u0( "http://nonexistent.example.com" ); + QUrl u1; + QVERIFY( u0.isValid() ); + QVERIFY( !u1.isValid() ); + nam.setCheckHasInternetUrl( { u1, u1, u1, u1 } ); + QCOMPARE( nam.getCheckInternetUrls().count(), 0 ); + nam.setCheckHasInternetUrl( { u1, u1, u0, u1 } ); + QCOMPARE( nam.getCheckInternetUrls().count(), 1 ); + } } From 67d2b5568d54a8cb2bcc42efb96a761987214561 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 14:40:52 +0200 Subject: [PATCH 47/87] [welcome] Fix test, check that the internet check URLs are loaded --- src/modules/welcome/Tests.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/welcome/Tests.cpp b/src/modules/welcome/Tests.cpp index eed7dae87..cc2b7b873 100644 --- a/src/modules/welcome/Tests.cpp +++ b/src/modules/welcome/Tests.cpp @@ -11,6 +11,7 @@ #include "Branding.h" #include "Settings.h" +#include "network/Manager.h" #include "utils/CalamaresUtilsSystem.h" #include "utils/Logger.h" #include "utils/Yaml.h" @@ -57,7 +58,7 @@ WelcomeTests::testOneUrl() // BUILD_AS_TEST is the source-directory path QString filename = QStringLiteral( "1a-checkinternet.conf" ); - QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) ); + QFile fi( QString( "%1/tests/%2" ).arg( BUILD_AS_TEST, filename ) ); QVERIFY( fi.exists() ); bool ok = false; @@ -65,6 +66,9 @@ WelcomeTests::testOneUrl() QVERIFY( ok ); QVERIFY( map.count() > 0 ); QVERIFY( map.contains( "requirements" ) ); + + c.setConfigurationMap( map ); + QCOMPARE( CalamaresUtils::Network::Manager::instance().getCheckInternetUrls().count(), 1 ); } From ad76a2cbe870ca9d67f050654e6add3dbfbe2fd5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 16:39:55 +0200 Subject: [PATCH 48/87] [welcome] [welcomeq] Move requirements to Config The Config object can hold all of the configuration information, including also the requirements-checking parts. Move requirements- checking configuration there, so it is shared and consistent across welcome and welcomeq, regardless. This repairs the test that expects the Config object to handle **all** of the configuration, too. --- src/modules/welcome/CMakeLists.txt | 25 +++++++++++---------- src/modules/welcome/Config.cpp | 12 ++++++++++ src/modules/welcome/Config.h | 6 +++++ src/modules/welcome/WelcomeViewStep.cpp | 15 +------------ src/modules/welcome/WelcomeViewStep.h | 1 - src/modules/welcomeq/WelcomeQmlViewStep.cpp | 18 +-------------- src/modules/welcomeq/WelcomeQmlViewStep.h | 3 --- 7 files changed, 33 insertions(+), 47 deletions(-) diff --git a/src/modules/welcome/CMakeLists.txt b/src/modules/welcome/CMakeLists.txt index 4895d4b80..01a89703a 100644 --- a/src/modules/welcome/CMakeLists.txt +++ b/src/modules/welcome/CMakeLists.txt @@ -8,26 +8,22 @@ find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED DBus Network ) find_package( LIBPARTED ) if ( LIBPARTED_FOUND ) set( PARTMAN_SRC checker/partman_devices.c ) - set( CHECKER_LINK_LIBRARIES ${LIBPARTED_LIBRARY} ) + set( PARTMAN_LIB ${LIBPARTED_LIBRARY} ) else() set( PARTMAN_SRC ) - set( CHECKER_LINK_LIBRARIES ) + set( PARTMAN_LIB ) add_definitions( -DWITHOUT_LIBPARTED ) endif() -set( CHECKER_SOURCES - checker/CheckerContainer.cpp - checker/GeneralRequirements.cpp - checker/ResultWidget.cpp - checker/ResultsListWidget.cpp - ${PARTMAN_SRC} -) - calamares_add_plugin( welcome TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES - ${CHECKER_SOURCES} + checker/CheckerContainer.cpp + checker/GeneralRequirements.cpp + checker/ResultWidget.cpp + checker/ResultsListWidget.cpp + ${PARTMAN_SRC} WelcomeViewStep.cpp Config.cpp Config.h @@ -37,7 +33,7 @@ calamares_add_plugin( welcome RESOURCES welcome.qrc LINK_PRIVATE_LIBRARIES - ${CHECKER_LINK_LIBRARIES} + ${PARTMAN_LIB} Qt5::DBus Qt5::Network SHARED_LIB @@ -46,9 +42,14 @@ calamares_add_plugin( welcome calamares_add_test( welcometest SOURCES + checker/GeneralRequirements.cpp + ${PARTMAN_SRC} Config.cpp Tests.cpp LIBRARIES + ${PARTMAN_LIB} + Qt5::DBus + Qt5::Network Qt5::Widgets Calamares::calamaresui ) diff --git a/src/modules/welcome/Config.cpp b/src/modules/welcome/Config.cpp index 989475153..c6b960d9d 100644 --- a/src/modules/welcome/Config.cpp +++ b/src/modules/welcome/Config.cpp @@ -27,6 +27,7 @@ Config::Config( QObject* parent ) : QObject( parent ) , m_languages( CalamaresUtils::Locale::availableTranslations() ) , m_filtermodel( std::make_unique< QSortFilterProxyModel >() ) + , m_requirementsChecker( std::make_unique< GeneralRequirements >( this ) ) { initLanguages(); @@ -399,4 +400,15 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) ::setLanguageIcon( this, configurationMap ); ::setGeoIP( this, configurationMap ); + + if ( configurationMap.contains( "requirements" ) + && configurationMap.value( "requirements" ).type() == QVariant::Map ) + { + m_requirementsChecker->setConfigurationMap( configurationMap.value( "requirements" ).toMap() ); + } + else + { + cWarning() << "no valid requirements map found in welcome " + "module configuration."; + } } diff --git a/src/modules/welcome/Config.h b/src/modules/welcome/Config.h index a3f1276a6..6cce75f22 100644 --- a/src/modules/welcome/Config.h +++ b/src/modules/welcome/Config.h @@ -10,6 +10,8 @@ #ifndef WELCOME_CONFIG_H #define WELCOME_CONFIG_H +#include "checker/GeneralRequirements.h" + #include "locale/LabelModel.h" #include "modulesystem/RequirementsModel.h" @@ -100,6 +102,9 @@ public slots: QAbstractItemModel* unsatisfiedRequirements() const; + /// @brief Check the general requirements + Calamares::RequirementsList checkRequirements() const { return m_requirementsChecker->checkRequirements(); } + signals: void countryCodeChanged( QString countryCode ); void localeIndexChanged( int localeIndex ); @@ -118,6 +123,7 @@ private: CalamaresUtils::Locale::LabelModel* m_languages = nullptr; std::unique_ptr< QSortFilterProxyModel > m_filtermodel; + std::unique_ptr< GeneralRequirements > m_requirementsChecker; QString m_languageIcon; QString m_countryCode; diff --git a/src/modules/welcome/WelcomeViewStep.cpp b/src/modules/welcome/WelcomeViewStep.cpp index 2a0d57bc4..df42271fc 100644 --- a/src/modules/welcome/WelcomeViewStep.cpp +++ b/src/modules/welcome/WelcomeViewStep.cpp @@ -12,7 +12,6 @@ #include "Config.h" #include "WelcomePage.h" -#include "checker/GeneralRequirements.h" #include "Branding.h" #include "modulesystem/ModuleManager.h" @@ -25,7 +24,6 @@ WelcomeViewStep::WelcomeViewStep( QObject* parent ) : Calamares::ViewStep( parent ) , m_conf( new Config( this ) ) , m_widget( new WelcomePage( m_conf ) ) - , m_requirementsChecker( new GeneralRequirements( this ) ) { connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, @@ -96,17 +94,6 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { m_conf->setConfigurationMap( configurationMap ); - if ( configurationMap.contains( "requirements" ) - && configurationMap.value( "requirements" ).type() == QVariant::Map ) - { - m_requirementsChecker->setConfigurationMap( configurationMap.value( "requirements" ).toMap() ); - } - else - { - cWarning() << "no valid requirements map found in welcome " - "module configuration."; - } - //here init the qml or qwidgets needed bits m_widget->init(); } @@ -114,5 +101,5 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap ) Calamares::RequirementsList WelcomeViewStep::checkRequirements() { - return m_requirementsChecker->checkRequirements(); + return m_conf->checkRequirements(); } diff --git a/src/modules/welcome/WelcomeViewStep.h b/src/modules/welcome/WelcomeViewStep.h index 57632f7ac..dfc6f1169 100644 --- a/src/modules/welcome/WelcomeViewStep.h +++ b/src/modules/welcome/WelcomeViewStep.h @@ -66,7 +66,6 @@ public: private: Config* m_conf; WelcomePage* m_widget; - GeneralRequirements* m_requirementsChecker; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeViewStepFactory ) diff --git a/src/modules/welcomeq/WelcomeQmlViewStep.cpp b/src/modules/welcomeq/WelcomeQmlViewStep.cpp index af32f2992..914ea4f3a 100644 --- a/src/modules/welcomeq/WelcomeQmlViewStep.cpp +++ b/src/modules/welcomeq/WelcomeQmlViewStep.cpp @@ -26,7 +26,6 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeQmlViewStepFactory, registerPlugin< WelcomeQmlViewStep::WelcomeQmlViewStep( QObject* parent ) : Calamares::QmlViewStep( parent ) , m_config( new Config( this ) ) - , m_requirementsChecker( new GeneralRequirements( this ) ) { connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, @@ -58,7 +57,6 @@ WelcomeQmlViewStep::isBackEnabled() const bool WelcomeQmlViewStep::isAtBeginning() const { - // TODO: adjust to "pages" in the QML return true; } @@ -66,7 +64,6 @@ WelcomeQmlViewStep::isAtBeginning() const bool WelcomeQmlViewStep::isAtEnd() const { - // TODO: adjust to "pages" in the QML return true; } @@ -81,26 +78,13 @@ void WelcomeQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { m_config->setConfigurationMap( configurationMap ); - - if ( configurationMap.contains( "requirements" ) - && configurationMap.value( "requirements" ).type() == QVariant::Map ) - { - m_requirementsChecker->setConfigurationMap( configurationMap.value( "requirements" ).toMap() ); - } - else - { - cWarning() << "no valid requirements map found in welcomeq " - "module configuration."; - } - Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last - setContextProperty( "Welcome", m_config ); } Calamares::RequirementsList WelcomeQmlViewStep::checkRequirements() { - return m_requirementsChecker->checkRequirements(); + return m_config->checkRequirements(); } QObject* diff --git a/src/modules/welcomeq/WelcomeQmlViewStep.h b/src/modules/welcomeq/WelcomeQmlViewStep.h index 4c68ea2ae..1ed90ce05 100644 --- a/src/modules/welcomeq/WelcomeQmlViewStep.h +++ b/src/modules/welcomeq/WelcomeQmlViewStep.h @@ -29,9 +29,7 @@ class Handler; } } // namespace CalamaresUtils -class GeneralRequirements; -// TODO: Needs a generic Calamares::QmlViewStep as base class // TODO: refactor and move what makes sense to base class class PLUGINDLLEXPORT WelcomeQmlViewStep : public Calamares::QmlViewStep { @@ -65,7 +63,6 @@ public: private: Config* m_config; - GeneralRequirements* m_requirementsChecker; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeQmlViewStepFactory ) From f376b42c31e4befd9d87c55ac80130bfd165f492 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 16:58:46 +0200 Subject: [PATCH 49/87] [welcome] Add a handful of tests for different URL configs --- src/modules/welcome/Tests.cpp | 37 +++++++++++++++++++ .../welcome/tests/1b-checkinternet.conf | 3 ++ .../welcome/tests/1c-checkinternet.conf | 4 ++ .../welcome/tests/1d-checkinternet.conf | 4 ++ .../welcome/tests/1e-checkinternet.conf | 4 ++ .../welcome/tests/1f-checkinternet.conf | 6 +++ .../welcome/tests/1g-checkinternet.conf | 3 ++ .../welcome/tests/1h-checkinternet.conf | 8 ++++ 8 files changed, 69 insertions(+) create mode 100644 src/modules/welcome/tests/1b-checkinternet.conf create mode 100644 src/modules/welcome/tests/1c-checkinternet.conf create mode 100644 src/modules/welcome/tests/1d-checkinternet.conf create mode 100644 src/modules/welcome/tests/1e-checkinternet.conf create mode 100644 src/modules/welcome/tests/1f-checkinternet.conf create mode 100644 src/modules/welcome/tests/1g-checkinternet.conf create mode 100644 src/modules/welcome/tests/1h-checkinternet.conf diff --git a/src/modules/welcome/Tests.cpp b/src/modules/welcome/Tests.cpp index cc2b7b873..0445433e9 100644 --- a/src/modules/welcome/Tests.cpp +++ b/src/modules/welcome/Tests.cpp @@ -29,6 +29,8 @@ private Q_SLOTS: void initTestCase(); void testOneUrl(); + void testUrls_data(); + void testUrls(); }; WelcomeTests::WelcomeTests() {} @@ -71,6 +73,41 @@ WelcomeTests::testOneUrl() QCOMPARE( CalamaresUtils::Network::Manager::instance().getCheckInternetUrls().count(), 1 ); } +void +WelcomeTests::testUrls_data() +{ + QTest::addColumn< QString >( "filename" ); + QTest::addColumn< int >( "result" ); + + QTest::newRow( "one " ) << QString( "1a-checkinternet.conf" ) << 1; + QTest::newRow( "none " ) << QString( "1b-checkinternet.conf" ) << 0; + QTest::newRow( "blank" ) << QString( "1c-checkinternet.conf" ) << 0; + QTest::newRow( "bogus" ) << QString( "1d-checkinternet.conf" ) << 0; + QTest::newRow( "[] " ) << QString( "1e-checkinternet.conf" ) << 0; + QTest::newRow( "-3 " ) << QString( "1f-checkinternet.conf" ) << 3; + QTest::newRow( "[3] " ) << QString( "1g-checkinternet.conf" ) << 3; + QTest::newRow( "some " ) << QString( "1h-checkinternet.conf" ) << 3; +} + +void +WelcomeTests::testUrls() +{ + QFETCH( QString, filename ); + QFETCH( int, result ); + + Config c; + + // BUILD_AS_TEST is the source-directory path + QFile fi( QString( "%1/tests/%2" ).arg( BUILD_AS_TEST, filename ) ); + QVERIFY( fi.exists() ); + + bool ok = false; + const auto map = CalamaresUtils::loadYaml( fi, &ok ); + QVERIFY( ok ); + + c.setConfigurationMap( map ); + QCOMPARE( CalamaresUtils::Network::Manager::instance().getCheckInternetUrls().count(), result ); +} QTEST_GUILESS_MAIN( WelcomeTests ) diff --git a/src/modules/welcome/tests/1b-checkinternet.conf b/src/modules/welcome/tests/1b-checkinternet.conf new file mode 100644 index 000000000..a1e656985 --- /dev/null +++ b/src/modules/welcome/tests/1b-checkinternet.conf @@ -0,0 +1,3 @@ +# Nothing at all +--- +bogus: 1 diff --git a/src/modules/welcome/tests/1c-checkinternet.conf b/src/modules/welcome/tests/1c-checkinternet.conf new file mode 100644 index 000000000..845e253c0 --- /dev/null +++ b/src/modules/welcome/tests/1c-checkinternet.conf @@ -0,0 +1,4 @@ +# Set to blank +--- +requirements: + internetCheckUrl: "" diff --git a/src/modules/welcome/tests/1d-checkinternet.conf b/src/modules/welcome/tests/1d-checkinternet.conf new file mode 100644 index 000000000..9a44d7c93 --- /dev/null +++ b/src/modules/welcome/tests/1d-checkinternet.conf @@ -0,0 +1,4 @@ +# Set to something broken +--- +requirements: + internetCheckUrl: false diff --git a/src/modules/welcome/tests/1e-checkinternet.conf b/src/modules/welcome/tests/1e-checkinternet.conf new file mode 100644 index 000000000..579414a79 --- /dev/null +++ b/src/modules/welcome/tests/1e-checkinternet.conf @@ -0,0 +1,4 @@ +# Empty list +--- +requirements: + internetCheckUrl: [] diff --git a/src/modules/welcome/tests/1f-checkinternet.conf b/src/modules/welcome/tests/1f-checkinternet.conf new file mode 100644 index 000000000..660760381 --- /dev/null +++ b/src/modules/welcome/tests/1f-checkinternet.conf @@ -0,0 +1,6 @@ +--- +requirements: + internetCheckUrl: + - http://example.com + - http://bogus.example.com + - http://nonexistent.example.com diff --git a/src/modules/welcome/tests/1g-checkinternet.conf b/src/modules/welcome/tests/1g-checkinternet.conf new file mode 100644 index 000000000..dd3ddae0c --- /dev/null +++ b/src/modules/welcome/tests/1g-checkinternet.conf @@ -0,0 +1,3 @@ +--- +requirements: + internetCheckUrl: [ http://example.com, http://bogus.example.com, http://nonexistent.example.com ] diff --git a/src/modules/welcome/tests/1h-checkinternet.conf b/src/modules/welcome/tests/1h-checkinternet.conf new file mode 100644 index 000000000..928360c20 --- /dev/null +++ b/src/modules/welcome/tests/1h-checkinternet.conf @@ -0,0 +1,8 @@ +# "0" is a valid URL (?) but "" is not +--- +requirements: + internetCheckUrl: + - http://example.com + - 0 + - "" + - http://nonexistent.example.com From 4e8d67052fea2e75d1273fbdaad6258d55d93f8a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 17:23:14 +0200 Subject: [PATCH 50/87] Changes: describe partition changes --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index a57b48956..f56ec0fc4 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,7 @@ website will have to do for older versions. This release contains contributions from (alphabetically by first name): - Anke Boersma + - Artem Grinev ## Core ## - No core changes yet @@ -19,6 +20,8 @@ This release contains contributions from (alphabetically by first name): - *networkcfg* now translates the "live user" on an ISO to the regular user on the installed system, so that network configuration changes made in the live system are automatically used after installation. + - *partition* no longer allows you to delete an extended partition with + children (which led to crashes). Thanks Artem! - *welcome* can now check multiple URLs to determine if internet connectivity is available. It is still recommended to check the distro home-page or some special "ping" page of the distro, although that has some privacy From 46ca4d93e76abd8ee8b5518b291dc170ca1d3063 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 17:30:55 +0200 Subject: [PATCH 51/87] [partition] Improve constness, naming --- src/modules/partition/gui/PartitionPage.cpp | 32 ++++++++++++--------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/modules/partition/gui/PartitionPage.cpp b/src/modules/partition/gui/PartitionPage.cpp index e4aa93c72..aa05c3a26 100644 --- a/src/modules/partition/gui/PartitionPage.cpp +++ b/src/modules/partition/gui/PartitionPage.cpp @@ -125,8 +125,8 @@ PartitionPage::~PartitionPage() {} void PartitionPage::updateButtons() { - bool create = false, createTable = false, edit = false, del = false, currentDeviceIsVG = false, - isDeactivable = false; + bool allow_create = false, allow_create_table = false, allow_edit = false, allow_delete = false; + bool currentDeviceIsVG = false, isDeactivable = false; bool isRemovable = false, isVGdeactivated = false; QModelIndex index = m_ui->partitionTreeView->currentIndex(); @@ -136,17 +136,21 @@ PartitionPage::updateButtons() Q_ASSERT( model ); Partition* partition = model->partitionForIndex( index ); Q_ASSERT( partition ); - bool isFree = CalamaresUtils::Partition::isPartitionFreeSpace( partition ); - bool isExtended = partition->roles().has( PartitionRole::Extended ); + const bool isFree = CalamaresUtils::Partition::isPartitionFreeSpace( partition ); + const bool isExtended = partition->roles().has( PartitionRole::Extended ); - bool hasChildren = isExtended + // An extended partition can have a "free space" child; that one does + // not count as a real child. If there are more children, at least one + // is a real one and we should not allow the extended partition to be + // deleted. + const bool hasChildren = isExtended && ( partition->children().length() > 1 || ( partition->children().length() == 1 && !CalamaresUtils::Partition::isPartitionFreeSpace( partition->children().at( 0 ) ) ) ); - bool isInVG = m_core->isInVG( partition ); + const bool isInVG = m_core->isInVG( partition ); - create = isFree; + allow_create = isFree; // Keep it simple for now: do not support editing extended partitions as // it does not work with our current edit implementation which is @@ -155,8 +159,8 @@ PartitionPage::updateButtons() // inside them, so an edit must be applied without altering the job // order. // TODO: See if LVM PVs can be edited in Calamares - edit = !isFree && !isExtended; - del = !isFree && !isInVG && !hasChildren; + allow_edit = !isFree && !isExtended; + allow_delete = !isFree && !isInVG && !hasChildren; } if ( m_ui->deviceComboBox->currentIndex() >= 0 ) @@ -173,7 +177,7 @@ PartitionPage::updateButtons() } else if ( device->type() != Device::Type::LVM_Device ) { - createTable = true; + allow_create_table = true; #ifdef WITH_KPMCORE4API if ( device->type() == Device::Type::SoftwareRAID_Device @@ -202,10 +206,10 @@ PartitionPage::updateButtons() } } - m_ui->createButton->setEnabled( create ); - m_ui->editButton->setEnabled( edit ); - m_ui->deleteButton->setEnabled( del ); - m_ui->newPartitionTableButton->setEnabled( createTable ); + m_ui->createButton->setEnabled( allow_create ); + m_ui->editButton->setEnabled( allow_edit ); + m_ui->deleteButton->setEnabled( allow_delete ); + m_ui->newPartitionTableButton->setEnabled( allow_create_table ); m_ui->resizeVolumeGroupButton->setEnabled( currentDeviceIsVG && !isVGdeactivated ); m_ui->deactivateVolumeGroupButton->setEnabled( currentDeviceIsVG && isDeactivable && !isVGdeactivated ); m_ui->removeVolumeGroupButton->setEnabled( currentDeviceIsVG && isRemovable ); From 52a82ea1e683139e5b71365899253ebd7ebf52b6 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Thu, 26 Aug 2021 17:39:06 +0200 Subject: [PATCH 52/87] [partition] Improve warning message in log --- src/modules/partition/core/PartUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/partition/core/PartUtils.cpp b/src/modules/partition/core/PartUtils.cpp index 4beac0db8..8fe13f505 100644 --- a/src/modules/partition/core/PartUtils.cpp +++ b/src/modules/partition/core/PartUtils.cpp @@ -461,7 +461,7 @@ isEfiFilesystemSuitable(const Partition* candidate) { return true; } - cWarning() << "FAT32 filesystem is too small (" << size << "bytes)"; + cWarning() << "FAT32 filesystem for EFI is too small (" << size << "bytes)"; return false; #ifdef WITH_KPMCORE4API case FileSystem::Type::Fat12: From 7d0877080655171755039b2dcd17c1cfcfcc55eb Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 27 Aug 2021 17:26:50 +0200 Subject: [PATCH 53/87] [partition] Apply code style --- .../partition/core/PartitionLayout.cpp | 98 ++++++++++--------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/modules/partition/core/PartitionLayout.cpp b/src/modules/partition/core/PartitionLayout.cpp index 233f5117a..8ae904e92 100644 --- a/src/modules/partition/core/PartitionLayout.cpp +++ b/src/modules/partition/core/PartitionLayout.cpp @@ -138,61 +138,63 @@ PartitionLayout::init( FileSystem::Type defaultFsType, const QVariantList& confi } void -PartitionLayout::setDefaultFsType(FileSystem::Type defaultFsType) +PartitionLayout::setDefaultFsType( FileSystem::Type defaultFsType ) { using FileSystem = FileSystem::Type; switch ( defaultFsType ) { - case FileSystem::Unknown: - case FileSystem::Unformatted: - case FileSystem::Extended: - case FileSystem::LinuxSwap: - case FileSystem::Luks: - case FileSystem::Ocfs2: - case FileSystem::Lvm2_PV: - case FileSystem::Udf: - case FileSystem::Iso9660: + case FileSystem::Unknown: + case FileSystem::Unformatted: + case FileSystem::Extended: + case FileSystem::LinuxSwap: + case FileSystem::Luks: + case FileSystem::Ocfs2: + case FileSystem::Lvm2_PV: + case FileSystem::Udf: + case FileSystem::Iso9660: #ifdef WITH_KPMCORE4API - case FileSystem::Luks2: - case FileSystem::LinuxRaidMember: - case FileSystem::BitLocker: + case FileSystem::Luks2: + case FileSystem::LinuxRaidMember: + case FileSystem::BitLocker: #endif - // bad bad - cWarning() << "The selected default FS" << defaultFsType << "is not suitable." << "Using ext4 instead."; - defaultFsType = FileSystem::Ext4; - break; - case FileSystem::Ext2: - case FileSystem::Ext3: - case FileSystem::Ext4: - case FileSystem::Fat32: - case FileSystem::Ntfs: - case FileSystem::Reiser4: - case FileSystem::ReiserFS: - case FileSystem::Xfs: - case FileSystem::Jfs: - case FileSystem::Btrfs: - case FileSystem::Exfat: - case FileSystem::F2fs: - // ok - break; - case FileSystem::Fat16: - case FileSystem::Hfs: - case FileSystem::HfsPlus: - case FileSystem::Ufs: - case FileSystem::Hpfs: - case FileSystem::Zfs: - case FileSystem::Nilfs2: + // bad bad + cWarning() << "The selected default FS" << defaultFsType << "is not suitable." + << "Using ext4 instead."; + defaultFsType = FileSystem::Ext4; + break; + case FileSystem::Ext2: + case FileSystem::Ext3: + case FileSystem::Ext4: + case FileSystem::Fat32: + case FileSystem::Ntfs: + case FileSystem::Reiser4: + case FileSystem::ReiserFS: + case FileSystem::Xfs: + case FileSystem::Jfs: + case FileSystem::Btrfs: + case FileSystem::Exfat: + case FileSystem::F2fs: + // ok + break; + case FileSystem::Fat16: + case FileSystem::Hfs: + case FileSystem::HfsPlus: + case FileSystem::Ufs: + case FileSystem::Hpfs: + case FileSystem::Zfs: + case FileSystem::Nilfs2: #ifdef WITH_KPMCORE4API - case FileSystem::Fat12: - case FileSystem::Apfs: - case FileSystem::Minix: + case FileSystem::Fat12: + case FileSystem::Apfs: + case FileSystem::Minix: #endif - // 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 = FileSystem::Ext4; + // 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 = FileSystem::Ext4; } m_defaultFsType = defaultFsType; @@ -278,7 +280,7 @@ PartitionLayout::createPartitions( Device* dev, } } - auto correctFS = [d=m_defaultFsType]( FileSystem::Type t ) { return t == FileSystem::Type::Unknown ? d : t; }; + auto correctFS = [d = m_defaultFsType]( FileSystem::Type t ) { return t == FileSystem::Type::Unknown ? d : t; }; // Create the partitions. currentSector = firstSector; From 6324fa3eb9ab1b5eab8b7eb5007368551c86bd4a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 27 Aug 2021 17:27:26 +0200 Subject: [PATCH 54/87] [partition] Disentangle questions of suitability of ESP - split into size, type, flags so the warning message can be tailored to what is wrong. --- src/modules/partition/core/PartUtils.cpp | 56 ++++++++++++++++-------- src/modules/partition/core/PartUtils.h | 19 +++++++- 2 files changed, 54 insertions(+), 21 deletions(-) diff --git a/src/modules/partition/core/PartUtils.cpp b/src/modules/partition/core/PartUtils.cpp index 8fe13f505..54f971f4e 100644 --- a/src/modules/partition/core/PartUtils.cpp +++ b/src/modules/partition/core/PartUtils.cpp @@ -447,31 +447,40 @@ isEfiSystem() } bool -isEfiFilesystemSuitable(const Partition* candidate) +isEfiFilesystemSuitableType( const Partition* candidate ) { auto type = candidate->fileSystem().type(); + + switch ( type ) + { + case FileSystem::Type::Fat32: + return true; +#ifdef WITH_KPMCORE4API + case FileSystem::Type::Fat12: +#endif + case FileSystem::Type::Fat16: + cWarning() << "FAT12 and FAT16 are probably not supported by EFI"; + return false; + default: + cWarning() << "EFI boot partition must be FAT32"; + return false; + } +} + +bool +isEfiFilesystemSuitableSize( const Partition* candidate ) +{ auto size = candidate->capacity(); // bytes using CalamaresUtils::Units::operator""_MiB; - - switch( type ) + if ( size >= 300_MiB ) { - case FileSystem::Type::Fat32: - if ( size >= 300_MiB ) - { - return true; - } - cWarning() << "FAT32 filesystem for EFI is too small (" << size << "bytes)"; - return false; -#ifdef WITH_KPMCORE4API - case FileSystem::Type::Fat12: -#endif - case FileSystem::Type::Fat16: - cWarning() << "FAT12 and FAT16 are probably not supported by EFI"; - return false; - default: - cWarning() << "EFI boot partition must be FAT32"; - return false; + return true; + } + else + { + cWarning() << "Filesystem for EFI is too small (" << size << "bytes)"; + return false; } } @@ -508,6 +517,15 @@ isEfiBootable( const Partition* candidate ) #endif } +// TODO: this is configurable via the config file **already** +size_t +efiFilesystemMinimumSize() +{ + using CalamaresUtils::Units::operator""_MiB; + return 300_MiB; +} + + QString canonicalFilesystemName( const QString& fsName, FileSystem::Type* fsType ) { diff --git a/src/modules/partition/core/PartUtils.h b/src/modules/partition/core/PartUtils.h index 6bf223921..dd4efc867 100644 --- a/src/modules/partition/core/PartUtils.h +++ b/src/modules/partition/core/PartUtils.h @@ -84,9 +84,24 @@ bool isEfiSystem(); /** * @brief Is the @p partition suitable as an EFI boot partition? - * Checks for filesystem type (FAT32) and size (300MiB at least). + * Checks for filesystem type (FAT32). */ -bool isEfiFilesystemSuitable( const Partition* candidate ); +bool isEfiFilesystemSuitableType( const Partition* candidate ); + +/** + * @brief Is the @p partition suitable as an EFI boot partition? + * Checks for filesystem size (300MiB, see efiFilesystemMinimumSize). + */ +bool isEfiFilesystemSuitableSize( const Partition* candidate ); + +/** @brief Returns the minimum size of an EFI boot partition. + * + * This is determined as 300MiB, based on the FAT32 standard + * and EFI documentation (and not a little discussion in Calamares + * issues about what works, what is effective, and what is mandated + * by the standard and how all of those are different). + */ +size_t efiFilesystemMinimumSize(); /** * @brief Is the given @p partition bootable in EFI? Depending on From da49becac341adf3ae5a46818d2f9d41ca457da4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 27 Aug 2021 17:28:07 +0200 Subject: [PATCH 55/87] [partition] Tailor warning message about ESP - tell the user all the things that are wrong with the (proposed) ESP; a missing one gets all the suggestions. --- src/modules/partition/PartitionViewStep.cpp | 74 +++++++++++++-------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/modules/partition/PartitionViewStep.cpp b/src/modules/partition/PartitionViewStep.cpp index d3d45bc8a..29e6524d9 100644 --- a/src/modules/partition/PartitionViewStep.cpp +++ b/src/modules/partition/PartitionViewStep.cpp @@ -434,50 +434,66 @@ PartitionViewStep::onLeave() { const QString espMountPoint = Calamares::JobQueue::instance()->globalStorage()->value( "efiSystemPartition" ).toString(); - const QString espFlagName = PartitionTable::flagName( #ifdef WITH_KPMCORE4API - PartitionTable::Flag::Boot + const auto espFlag = PartitionTable::Flag::Boot; #else - PartitionTable::FlagEsp + const auto espFlag = PartitionTable::FlagEsp; #endif - ); Partition* esp = m_core->findPartitionByMountPoint( espMountPoint ); QString message; QString description; - if ( !esp || ( esp && !PartUtils::isEfiFilesystemSuitable( esp ) ) ) + + Logger::Once o; + + const bool okType = esp && PartUtils::isEfiFilesystemSuitableType( esp ); + const bool okSize = esp && PartUtils::isEfiFilesystemSuitableSize( esp ); + const bool okFlag = esp && PartUtils::isEfiBootable( esp ); + + if ( !esp ) { message = tr( "No EFI system partition configured" ); - description = tr( "An EFI system partition is necessary to start %1." - "

    " - "To configure an EFI system partition, go back and " - "select or create a FAT32 filesystem with the " - "%3 flag enabled and mount point " - "%2.

    " - "You can continue without setting up an EFI system " - "partition but your system may fail to start." ) - .arg( branding->shortProductName() ) - .arg( espMountPoint, espFlagName ); } - else if ( esp && !PartUtils::isEfiBootable( esp ) ) + else if ( !(okType && okSize && okFlag ) ) { - message = tr( "EFI system partition flag not set" ); - description = tr( "An EFI system partition is necessary to start %1." - "

    " - "A partition was configured with mount point " - "%2 but its %3 " - "flag is not set.
    " - "To set the flag, go back and edit the partition." - "

    " - "You can continue without setting the flag but your " - "system may fail to start." ) - .arg( branding->shortProductName() ) - .arg( espMountPoint, espFlagName ); + message = tr( "EFI system partition configured incorrectly" ); } + if ( !esp || !(okType&&okSize &&okFlag)) { + description = tr( "An EFI system partition is necessary to start %1." + "

    " + "To configure an EFI system partition, go back and " + "select or create a suitable filesystem.").arg( branding->shortProductName() ); + } + if (!esp) { + cDebug() << o << "No ESP mounted"; + description.append(' '); + description.append(tr("The filesystem must be mounted on %1.").arg(espMountPoint)); + } + if (!okType) { + cDebug() << o << "ESP wrong type"; + description.append(' '); + description.append(tr("The filesystem must have type FAT32.")); + } + if (!okSize) { + cDebug() << o << "ESP too small"; + description.append(' '); + description.append(tr("The filesystem must be at least %1 MiB in size.").arg( PartUtils::efiFilesystemMinimumSize() )); + } + if (!okFlag) + { + cDebug() << o << "ESP missing flag"; + description.append(' '); + description.append(tr("The filesystem must have flag %1 set.").arg(PartitionTable::flagName( espFlag ))); + } + if (!description.isEmpty()) { + description.append( "

    " ); + description.append( tr( + "You can continue without setting up an EFI system " + "partition but your system may fail to start." )); + } if ( !message.isEmpty() ) { - cWarning() << message; QMessageBox::warning( m_manualPartitionPage, message, description ); } } From 4bf3afac482450967e01aa2a82995aefada4528e Mon Sep 17 00:00:00 2001 From: waneon Date: Fri, 27 Aug 2021 19:18:09 -0400 Subject: [PATCH 56/87] [partition] Fix invalid variable name --- src/modules/partition/gui/PartitionPage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/partition/gui/PartitionPage.cpp b/src/modules/partition/gui/PartitionPage.cpp index aa05c3a26..d22f6f01d 100644 --- a/src/modules/partition/gui/PartitionPage.cpp +++ b/src/modules/partition/gui/PartitionPage.cpp @@ -183,8 +183,8 @@ PartitionPage::updateButtons() if ( device->type() == Device::Type::SoftwareRAID_Device && static_cast< SoftwareRAID* >( device )->status() == SoftwareRAID::Status::Inactive ) { - createTable = false; - create = false; + allow_create_table = false; + allow_create = false; } #endif } From 6831fac76be30b155d96a3e0d4c94093eb0c6e1f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 30 Aug 2021 22:34:57 +0200 Subject: [PATCH 57/87] Changes: document newly-merged --- CHANGES | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index f56ec0fc4..61e3e338f 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ website will have to do for older versions. This release contains contributions from (alphabetically by first name): - Anke Boersma - Artem Grinev + - Waneon Kim (new contributor, welcome!) ## Core ## - No core changes yet @@ -19,14 +20,16 @@ This release contains contributions from (alphabetically by first name): ## Modules ## - *networkcfg* now translates the "live user" on an ISO to the regular user on the installed system, so that network configuration changes - made in the live system are automatically used after installation. + made in the live system are automatically used after installation. #1755 - *partition* no longer allows you to delete an extended partition with - children (which led to crashes). Thanks Artem! + children (which led to crashes). Thanks Artem! #1749 + - *partition* complains in more detail about the state of the UEFI + boot partition (under manual partitioning schemes). #1761 - *welcome* can now check multiple URLs to determine if internet connectivity is available. It is still recommended to check the distro home-page or some special "ping" page of the distro, although that has some privacy implications; using example.com or google.com may work as well. Listing - multiple URLs will ping each of them in turn until one succeeds. + multiple URLs will ping each of them in turn until one succeeds. #1669 # 3.2.41.1 (2021-08-05) # From 0899eefde7c70dd9434875f0988aa092debf4ff3 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 11:33:05 +0200 Subject: [PATCH 58/87] [packagechooserq] Fix build on Debian, openSUSE with AppStream --- src/modules/packagechooserq/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/packagechooserq/CMakeLists.txt b/src/modules/packagechooserq/CMakeLists.txt index 0d98c7d1e..51a17e215 100644 --- a/src/modules/packagechooserq/CMakeLists.txt +++ b/src/modules/packagechooserq/CMakeLists.txt @@ -28,7 +28,7 @@ if ( WITH_APPDATA ) if ( Qt5Xml_FOUND ) add_definitions( -DHAVE_APPDATA ) list( APPEND _extra_libraries Qt5::Xml ) - list( APPEND _extra_src ItemAppData.cpp ) + list( APPEND _extra_src ${_packagechooser}/ItemAppData.cpp ) endif() endif() @@ -48,7 +48,7 @@ if ( WITH_APPSTREAM ) if ( AppStreamQt_FOUND ) add_definitions( -DHAVE_APPSTREAM ) list( APPEND _extra_libraries AppStreamQt ) - list( APPEND _extra_src ItemAppStream.cpp ) + list( APPEND _extra_src ${_packagechooser}/ItemAppStream.cpp ) endif() endif() @@ -59,7 +59,7 @@ calamares_add_plugin( packagechooserq PackageChooserQmlViewStep.cpp ${_packagechooser}/Config.cpp ${_packagechooser}/PackageModel.cpp - ${_packagechooser}/ItemAppData.cpp + ${_extra_src} RESOURCES packagechooserq.qrc LINK_PRIVATE_LIBRARIES From 3519697d0e72c03307ca3056f47c6d70a6d5987d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 12:54:51 +0200 Subject: [PATCH 59/87] [libcalamares] Slightly more memory-safe Use unique_ptr to ensure Private is always deleted. SEE #1758 --- src/libcalamares/partition/Mount.cpp | 7 ++----- src/libcalamares/partition/Mount.h | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/libcalamares/partition/Mount.cpp b/src/libcalamares/partition/Mount.cpp index 0fd204df4..89e17a885 100644 --- a/src/libcalamares/partition/Mount.cpp +++ b/src/libcalamares/partition/Mount.cpp @@ -92,7 +92,7 @@ struct TemporaryMount::Private TemporaryMount::TemporaryMount( const QString& devicePath, const QString& filesystemName, const QString& options ) - : m_d( new Private ) + : m_d( std::make_unique() ) { m_d->m_devicePath = devicePath; m_d->m_mountDir.setAutoRemove( false ); @@ -100,8 +100,7 @@ TemporaryMount::TemporaryMount( const QString& devicePath, const QString& filesy if ( r ) { cWarning() << "Mount of" << devicePath << "on" << m_d->m_mountDir.path() << "failed, code" << r; - delete m_d; - m_d = nullptr; + m_d.reset(); } } @@ -115,8 +114,6 @@ TemporaryMount::~TemporaryMount() cWarning() << "UnMount of temporary" << m_d->m_devicePath << "on" << m_d->m_mountDir.path() << "failed, code" << r; } - delete m_d; - m_d = nullptr; } } diff --git a/src/libcalamares/partition/Mount.h b/src/libcalamares/partition/Mount.h index 6a2ef9f8b..d088b108f 100644 --- a/src/libcalamares/partition/Mount.h +++ b/src/libcalamares/partition/Mount.h @@ -17,6 +17,8 @@ #include #include +#include + namespace CalamaresUtils { namespace Partition @@ -58,12 +60,12 @@ public: TemporaryMount& operator=( const TemporaryMount& ) = delete; ~TemporaryMount(); - bool isValid() const { return m_d; } + bool isValid() const { return bool( m_d ); } QString path() const; private: struct Private; - Private* m_d = nullptr; + std::unique_ptr< Private > m_d; }; } // namespace Partition From ede19c8a6179a4e8cd049317663844cfa586b10b Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 13:45:15 +0200 Subject: [PATCH 60/87] [partition] Fix build of devices test --- src/modules/partition/tests/CMakeLists.txt | 5 ++++- src/modules/partition/tests/DevicesTests.cpp | 22 +++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/modules/partition/tests/CMakeLists.txt b/src/modules/partition/tests/CMakeLists.txt index bb0b59af4..2839270fb 100644 --- a/src/modules/partition/tests/CMakeLists.txt +++ b/src/modules/partition/tests/CMakeLists.txt @@ -44,12 +44,12 @@ calamares_add_test( calamares_add_test( partitioncreatelayoutstest SOURCES + CreateLayoutsTests.cpp ${PartitionModule_SOURCE_DIR}/core/KPMHelpers.cpp ${PartitionModule_SOURCE_DIR}/core/PartitionInfo.cpp ${PartitionModule_SOURCE_DIR}/core/PartitionLayout.cpp ${PartitionModule_SOURCE_DIR}/core/PartUtils.cpp ${PartitionModule_SOURCE_DIR}/core/DeviceModel.cpp - CreateLayoutsTests.cpp LIBRARIES kpmcore Calamares::calamaresui @@ -67,4 +67,7 @@ calamares_add_test( partitiondevicestest SOURCES DevicesTests.cpp + ${PartitionModule_SOURCE_DIR}/core/DeviceList.cpp + LIBRARIES + kpmcore ) diff --git a/src/modules/partition/tests/DevicesTests.cpp b/src/modules/partition/tests/DevicesTests.cpp index 82311b288..02b1dc62e 100644 --- a/src/modules/partition/tests/DevicesTests.cpp +++ b/src/modules/partition/tests/DevicesTests.cpp @@ -11,6 +11,9 @@ #include "utils/Logger.h" +#include +#include + #include #include @@ -33,14 +36,13 @@ DevicesTests::testKPMScanDevices() { Logger::setupLogLevel( Logger::LOGVERBOSE ); -#if defined( WITH_KPMCORE4API ) cDebug() << "Getting devices via KPMCore"; CoreBackend* backend = CoreBackendManager::self()->backend(); - DeviceList devices = backend->scanDevices( /* not includeReadOnly, not includeLoopback */ ScanFlag( 0 ) ); + QVERIFY( backend ); + auto devices = backend->scanDevices(); // Whatever the default is /* not includeReadOnly, not includeLoopback */ ScanFlag( 0 ) ); cDebug() << Logger::SubEntry << "Done getting devices."; -#else - cWarning() << "Test skipped; use KPMCore4"; -#endif + + QVERIFY( devices.count() > 0 ); } void @@ -49,8 +51,14 @@ DevicesTests::testPartUtilScanDevices() Logger::setupLogLevel( Logger::LOGVERBOSE ); cDebug() << "Getting devices via PartUtils"; - auto l = PartUtils::getDevices(); + auto devices = PartUtils::getDevices(); cDebug() << Logger::SubEntry << "Done getting devices."; - QVERIFY( l.count() > 0 ); + QVERIFY( devices.count() > 0 ); } + +QTEST_GUILESS_MAIN( DevicesTests ) + +#include "utils/moc-warnings.h" + +#include "DevicesTests.moc" From 3d0709c779be330ae3259dff9a80c431b944f3c2 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 13:46:25 +0200 Subject: [PATCH 61/87] [partition] Avoid crash when there is no KPM backend --- src/modules/partition/core/DeviceList.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/partition/core/DeviceList.cpp b/src/modules/partition/core/DeviceList.cpp index 6b770a982..c7de12e88 100644 --- a/src/modules/partition/core/DeviceList.cpp +++ b/src/modules/partition/core/DeviceList.cpp @@ -112,6 +112,11 @@ QList< Device* > getDevices( DeviceType which ) { CoreBackend* backend = CoreBackendManager::self()->backend(); + if ( !backend ) + { + cWarning() << "No KPM backend found."; + return {}; + } #if defined( WITH_KPMCORE4API ) DeviceList devices = backend->scanDevices( /* not includeReadOnly, not includeLoopback */ ScanFlag( 0 ) ); #else From 1554c3a07cbf3779589d923778e0811e56bef1f2 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 13:54:23 +0200 Subject: [PATCH 62/87] [partition] Create KPM backend at start of test --- src/modules/partition/tests/DevicesTests.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/modules/partition/tests/DevicesTests.cpp b/src/modules/partition/tests/DevicesTests.cpp index 02b1dc62e..6d42e71e4 100644 --- a/src/modules/partition/tests/DevicesTests.cpp +++ b/src/modules/partition/tests/DevicesTests.cpp @@ -9,6 +9,7 @@ #include "core/DeviceList.h" +#include "partition/KPMManager.h" #include "utils/Logger.h" #include @@ -17,6 +18,8 @@ #include #include +#include + class DevicesTests : public QObject { Q_OBJECT @@ -27,9 +30,13 @@ public: private Q_SLOTS: void testKPMScanDevices(); void testPartUtilScanDevices(); + +private: + std::unique_ptr< CalamaresUtils::Partition::KPMManager > m_d; }; -DevicesTests::DevicesTests() {} +DevicesTests::DevicesTests() : m_d( std::make_unique< CalamaresUtils::Partition::KPMManager >() ) +{} void DevicesTests::testKPMScanDevices() @@ -39,7 +46,12 @@ DevicesTests::testKPMScanDevices() cDebug() << "Getting devices via KPMCore"; CoreBackend* backend = CoreBackendManager::self()->backend(); QVERIFY( backend ); - auto devices = backend->scanDevices(); // Whatever the default is /* not includeReadOnly, not includeLoopback */ ScanFlag( 0 ) ); +#if defined( WITH_KPMCORE4API ) + auto flags = ScanFlag( ~0 ); +#else + auto flags = true; +#endif + auto devices = backend->scanDevices( flags ); // These flags try to get "all" cDebug() << Logger::SubEntry << "Done getting devices."; QVERIFY( devices.count() > 0 ); From 09491e4cbb70b78ad1462d729d4fe816dcf9b9bd Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 13:58:23 +0200 Subject: [PATCH 63/87] [partition] expect failures when kpm can't read the disk --- src/modules/partition/tests/DevicesTests.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/modules/partition/tests/DevicesTests.cpp b/src/modules/partition/tests/DevicesTests.cpp index 6d42e71e4..6513e955b 100644 --- a/src/modules/partition/tests/DevicesTests.cpp +++ b/src/modules/partition/tests/DevicesTests.cpp @@ -20,6 +20,8 @@ #include +#include + class DevicesTests : public QObject { Q_OBJECT @@ -33,9 +35,12 @@ private Q_SLOTS: private: std::unique_ptr< CalamaresUtils::Partition::KPMManager > m_d; + bool m_isRoot = false; }; -DevicesTests::DevicesTests() : m_d( std::make_unique< CalamaresUtils::Partition::KPMManager >() ) +DevicesTests::DevicesTests() + : m_d( std::make_unique< CalamaresUtils::Partition::KPMManager >() ) + , m_isRoot( geteuid() == 0 ) {} void @@ -54,6 +59,10 @@ DevicesTests::testKPMScanDevices() auto devices = backend->scanDevices( flags ); // These flags try to get "all" cDebug() << Logger::SubEntry << "Done getting devices."; + if ( !m_isRoot ) + { + QEXPECT_FAIL( "", "Test invalid when not root", Continue ); + } QVERIFY( devices.count() > 0 ); } @@ -66,6 +75,10 @@ DevicesTests::testPartUtilScanDevices() auto devices = PartUtils::getDevices(); cDebug() << Logger::SubEntry << "Done getting devices."; + if ( !m_isRoot ) + { + QEXPECT_FAIL( "", "Test invalid when not root", Continue ); + } QVERIFY( devices.count() > 0 ); } From 2f88ba0d3f2d1df4a35df67d5557d2c0f1ea2062 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 31 Aug 2021 14:03:04 +0200 Subject: [PATCH 64/87] [partition] Coding style --- src/modules/partition/tests/DevicesTests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/partition/tests/DevicesTests.cpp b/src/modules/partition/tests/DevicesTests.cpp index 6513e955b..c63d7476d 100644 --- a/src/modules/partition/tests/DevicesTests.cpp +++ b/src/modules/partition/tests/DevicesTests.cpp @@ -41,7 +41,8 @@ private: DevicesTests::DevicesTests() : m_d( std::make_unique< CalamaresUtils::Partition::KPMManager >() ) , m_isRoot( geteuid() == 0 ) -{} +{ +} void DevicesTests::testKPMScanDevices() @@ -56,7 +57,7 @@ DevicesTests::testKPMScanDevices() #else auto flags = true; #endif - auto devices = backend->scanDevices( flags ); // These flags try to get "all" + auto devices = backend->scanDevices( flags ); // These flags try to get "all" cDebug() << Logger::SubEntry << "Done getting devices."; if ( !m_isRoot ) From 57f2a4c9eb85bdb70e6ef2cb127af182afd929f7 Mon Sep 17 00:00:00 2001 From: demmm Date: Tue, 31 Aug 2021 22:38:48 +0200 Subject: [PATCH 65/87] update CHANGES with what was done for QML modules --- CHANGES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index 61e3e338f..76d430a97 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,9 @@ This release contains contributions from (alphabetically by first name): - No core changes yet ## Modules ## + - The *localeq* module had the i18n.qml rewritten to make it easier + to customize. A bug in the layout has been fixed, and the overall + look has been updated. - *networkcfg* now translates the "live user" on an ISO to the regular user on the installed system, so that network configuration changes made in the live system are automatically used after installation. #1755 @@ -30,6 +33,12 @@ This release contains contributions from (alphabetically by first name): some special "ping" page of the distro, although that has some privacy implications; using example.com or google.com may work as well. Listing multiple URLs will ping each of them in turn until one succeeds. #1669 + - The work to make a QML version available for all view modules is almost + completed. Two new QML modules have been added *packagechooserq* and *summaryq*. + Summaryq brings the option to present the summary page in a customizable + way, with a bit more of a contemporary look. Packagechooserq adds the option + to preselect an item and displays all options in one overview. + # 3.2.41.1 (2021-08-05) # From 35156574b82edff5c4c76e2228c018861dec25f4 Mon Sep 17 00:00:00 2001 From: dr460nf1r3 Date: Fri, 3 Sep 2021 20:49:08 +0200 Subject: [PATCH 66/87] Dont set fsck check for BTRFS in fstab --- src/modules/fstab/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/fstab/main.py b/src/modules/fstab/main.py index 61f6b0e9f..5312e7f5b 100644 --- a/src/modules/fstab/main.py +++ b/src/modules/fstab/main.py @@ -244,9 +244,9 @@ class FstabGenerator(object): if extra: options += "," + extra - if mount_point == "/": + if mount_point == "/" and filesystem != "btrfs": check = 1 - elif mount_point and mount_point != "swap": + elif mount_point and mount_point != "swap" and filesystem != "btrfs": check = 2 else: check = 0 From 1a1009a2a9e866bc596ba7d701549fa58b1df1e7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 17:07:32 +0200 Subject: [PATCH 67/87] [summaryq] add SPDX-tags --- src/modules/summaryq/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/summaryq/CMakeLists.txt b/src/modules/summaryq/CMakeLists.txt index cdf520b24..8aac1bc2f 100644 --- a/src/modules/summaryq/CMakeLists.txt +++ b/src/modules/summaryq/CMakeLists.txt @@ -1,3 +1,8 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2021 Anke Boersma +# SPDX-License-Identifier: BSD-2-Clause +# if( NOT WITH_QML ) calamares_skip_module( "summaryq (QML is not supported in this build)" ) return() From 47c504df5daed636664ec1828b4b6dd0051627a4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 17:07:49 +0200 Subject: [PATCH 68/87] [welcome] add SPDX-tags to test-data --- src/modules/welcome/tests/1a-checkinternet.conf | 2 ++ src/modules/welcome/tests/1b-checkinternet.conf | 3 +++ src/modules/welcome/tests/1c-checkinternet.conf | 3 +++ src/modules/welcome/tests/1d-checkinternet.conf | 3 +++ src/modules/welcome/tests/1e-checkinternet.conf | 3 +++ src/modules/welcome/tests/1f-checkinternet.conf | 6 +++++- src/modules/welcome/tests/1g-checkinternet.conf | 4 ++++ src/modules/welcome/tests/1h-checkinternet.conf | 5 ++++- 8 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/modules/welcome/tests/1a-checkinternet.conf b/src/modules/welcome/tests/1a-checkinternet.conf index d2140f201..d10a97d2a 100644 --- a/src/modules/welcome/tests/1a-checkinternet.conf +++ b/src/modules/welcome/tests/1a-checkinternet.conf @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 --- requirements: internetCheckUrl: http://example.com diff --git a/src/modules/welcome/tests/1b-checkinternet.conf b/src/modules/welcome/tests/1b-checkinternet.conf index a1e656985..7cb9b4206 100644 --- a/src/modules/welcome/tests/1b-checkinternet.conf +++ b/src/modules/welcome/tests/1b-checkinternet.conf @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# # Nothing at all --- bogus: 1 diff --git a/src/modules/welcome/tests/1c-checkinternet.conf b/src/modules/welcome/tests/1c-checkinternet.conf index 845e253c0..6dbfb8f37 100644 --- a/src/modules/welcome/tests/1c-checkinternet.conf +++ b/src/modules/welcome/tests/1c-checkinternet.conf @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# # Set to blank --- requirements: diff --git a/src/modules/welcome/tests/1d-checkinternet.conf b/src/modules/welcome/tests/1d-checkinternet.conf index 9a44d7c93..0f5896ce1 100644 --- a/src/modules/welcome/tests/1d-checkinternet.conf +++ b/src/modules/welcome/tests/1d-checkinternet.conf @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# # Set to something broken --- requirements: diff --git a/src/modules/welcome/tests/1e-checkinternet.conf b/src/modules/welcome/tests/1e-checkinternet.conf index 579414a79..98ff62695 100644 --- a/src/modules/welcome/tests/1e-checkinternet.conf +++ b/src/modules/welcome/tests/1e-checkinternet.conf @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# # Empty list --- requirements: diff --git a/src/modules/welcome/tests/1f-checkinternet.conf b/src/modules/welcome/tests/1f-checkinternet.conf index 660760381..158025c62 100644 --- a/src/modules/welcome/tests/1f-checkinternet.conf +++ b/src/modules/welcome/tests/1f-checkinternet.conf @@ -1,6 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Multiple, all valid --- requirements: - internetCheckUrl: + internetCheckUrl: - http://example.com - http://bogus.example.com - http://nonexistent.example.com diff --git a/src/modules/welcome/tests/1g-checkinternet.conf b/src/modules/welcome/tests/1g-checkinternet.conf index dd3ddae0c..1f4477f9f 100644 --- a/src/modules/welcome/tests/1g-checkinternet.conf +++ b/src/modules/welcome/tests/1g-checkinternet.conf @@ -1,3 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Multiple, all valid, in short-list form --- requirements: internetCheckUrl: [ http://example.com, http://bogus.example.com, http://nonexistent.example.com ] diff --git a/src/modules/welcome/tests/1h-checkinternet.conf b/src/modules/welcome/tests/1h-checkinternet.conf index 928360c20..4984cf12b 100644 --- a/src/modules/welcome/tests/1h-checkinternet.conf +++ b/src/modules/welcome/tests/1h-checkinternet.conf @@ -1,7 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# # "0" is a valid URL (?) but "" is not --- requirements: - internetCheckUrl: + internetCheckUrl: - http://example.com - 0 - "" From c367731c42f5fd1cdeb9206c0010ff7ad98bf3c4 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 17:29:21 +0200 Subject: [PATCH 69/87] [packagechooser] Rename internals - pkgc -> packageChoice and similar for methods, variables - document that this is the convenience value for one-selection QML modules, not a full model - use std::optional to keep track of which one is being used. --- src/modules/packagechooser/Config.cpp | 23 +++++++++++++++-------- src/modules/packagechooser/Config.h | 26 ++++++++++++++++++++------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/modules/packagechooser/Config.cpp b/src/modules/packagechooser/Config.cpp index f66f16824..2c798ea03 100644 --- a/src/modules/packagechooser/Config.cpp +++ b/src/modules/packagechooser/Config.cpp @@ -100,10 +100,10 @@ Config::updateGlobalStorage( const QStringList& selected ) const { if ( m_method == PackageChooserMethod::Legacy ) { - //QString value = selected.join( ',' ); - QString value = ( m_pkgc ); + QString value = selected.join( ',' ); + // QString value = ( m_pkgc ); Calamares::JobQueue::instance()->globalStorage()->insert( m_id, value ); - cDebug() << m_id<< "selected" << value; + cDebug() << m_id << "selected" << value; } else if ( m_method == PackageChooserMethod::Packages ) { @@ -119,16 +119,23 @@ Config::updateGlobalStorage( const QStringList& selected ) const } void -Config::setPkgc( const QString& pkgc ) +Config::setPackageChoice( const QString& packageChoice ) { - m_pkgc = pkgc; - emit pkgcChanged( m_pkgc ); + if ( packageChoice.isEmpty() ) + { + m_packageChoice.reset(); + } + else + { + m_packageChoice = packageChoice; + } + emit packageChoiceChanged( m_packageChoice.value_or( QString() ) ); } QString Config::prettyStatus() const { - return tr( "Install option: %1" ).arg( m_pkgc ); + return tr( "Install option: %1" ).arg( m_packageChoice.value_or( tr( "None" ) ) ); } static void @@ -197,7 +204,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) PackageChooserMode::Required ); m_method = PackageChooserMethodNames().find( CalamaresUtils::getString( configurationMap, "method" ), PackageChooserMethod::Legacy ); - m_pkgc = CalamaresUtils::getString( configurationMap, "pkgc" ); + setPackageChoice( CalamaresUtils::getString( configurationMap, "pkgc" ) ); if ( m_method == PackageChooserMethod::Legacy ) { diff --git a/src/modules/packagechooser/Config.h b/src/modules/packagechooser/Config.h index b343a8cb2..75ff0d0c6 100644 --- a/src/modules/packagechooser/Config.h +++ b/src/modules/packagechooser/Config.h @@ -17,6 +17,7 @@ #include "modulesystem/InstanceKey.h" #include +#include enum class PackageChooserMode { @@ -40,7 +41,16 @@ class Config : public Calamares::ModuleSystem::Config { Q_OBJECT - Q_PROPERTY( QString pkgc READ pkgc WRITE setPkgc NOTIFY pkgcChanged ) + /** @brief This is the single-select package-choice + * + * For (QML) modules that support only a single selection and + * just want to do things in a straightforward pick-this-one + * way, the packageChoice property is a (the) way to go. + * + * Writing to this property means that any other form of package- + * choice or selection is ignored. + */ + Q_PROPERTY( QString packageChoice READ packageChoice WRITE setPackageChoice NOTIFY packageChoiceChanged ) Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL ) public: @@ -78,13 +88,13 @@ public: /// As updateGlobalStorage() with an empty selection list void fillGSSecondaryConfiguration() const { updateGlobalStorage( QStringList() ); } - QString pkgc() const { return m_pkgc; } - void setPkgc( const QString& pkgc ); + QString packageChoice() const { return m_packageChoice.value_or( QString() ); } + void setPackageChoice( const QString& packageChoice ); QString prettyStatus() const; signals: - void pkgcChanged( QString pkgc ); + void packageChoiceChanged( QString packageChoice ); void prettyStatusChanged(); private: @@ -99,8 +109,12 @@ private: QString m_id; /// Value to use for id if none is set in the config file Calamares::ModuleSystem::InstanceKey m_defaultId; - /// QML selection - QString m_pkgc; + /** @brief QML selection (for single-selection approaches) + * + * If there is no value, then there has been no selection. + * Reading the property will return an empty QString. + */ + std::optional< QString > m_packageChoice; }; From 8b703ba6a33c5bbf21909e348d28f54adbcaa53d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 21:12:11 +0200 Subject: [PATCH 70/87] [packagechooserq] More docs for the config file --- src/modules/packagechooserq/packagechooserq.conf | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/modules/packagechooserq/packagechooserq.conf b/src/modules/packagechooserq/packagechooserq.conf index fc45e4fa9..ecf9ed1a6 100644 --- a/src/modules/packagechooserq/packagechooserq.conf +++ b/src/modules/packagechooserq/packagechooserq.conf @@ -6,9 +6,7 @@ # Software selection mode, to set whether the software packages # can be chosen singly, or multiply. # -# Possible modes are "optional", "required" (for zero-or-one or exactly-one) -# or "optionalmultiple", "requiredmultiple" (for zero-or-more -# or one-or-more). +# The example QML module does not use a model, and ignores this value. mode: required # Software installation method: @@ -40,7 +38,11 @@ mode: required # modules, either *contextualprocess* or *packages* or some custom # module, in the `exec` section to do the actual work. method: legacy -# The *id* key is used only in "legacy" mode + +# The *id* key is used only in "legacy" mode and changes the GlobalStorage +# key used to store the package choices. It is not recommended to use +# this, since the module instance key does the same job. +# # id: "" # The *pkgc* is used for setting the default selection in the QML view From 8a49fde0163c6660784d2ea11da817e721a8f36b Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 21:15:57 +0200 Subject: [PATCH 71/87] [packagechooserq] Apply renaming also to distro-visible code - The distro-visible key is renamed *pkgc* to *packageChoice* - Read *packageChoice* only if there are no items to use --- src/modules/packagechooser/Config.cpp | 27 ++++++++++--------- .../packagechooserq/packagechooserq.conf | 10 +++++-- .../packagechooserq/packagechooserq.qml | 8 +++--- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/modules/packagechooser/Config.cpp b/src/modules/packagechooser/Config.cpp index 2c798ea03..11288bde6 100644 --- a/src/modules/packagechooser/Config.cpp +++ b/src/modules/packagechooser/Config.cpp @@ -204,7 +204,6 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) PackageChooserMode::Required ); m_method = PackageChooserMethodNames().find( CalamaresUtils::getString( configurationMap, "method" ), PackageChooserMethod::Legacy ); - setPackageChoice( CalamaresUtils::getString( configurationMap, "pkgc" ) ); if ( m_method == PackageChooserMethod::Legacy ) { @@ -233,21 +232,25 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) if ( configurationMap.contains( "items" ) ) { fillModel( m_model, configurationMap.value( "items" ).toList() ); - } - QString default_item_id = CalamaresUtils::getString( configurationMap, "default" ); - if ( !default_item_id.isEmpty() ) - { - for ( int item_n = 0; item_n < m_model->packageCount(); ++item_n ) + QString default_item_id = CalamaresUtils::getString( configurationMap, "default" ); + if ( !default_item_id.isEmpty() ) { - QModelIndex item_idx = m_model->index( item_n, 0 ); - QVariant item_id = m_model->data( item_idx, PackageListModel::IdRole ); - - if ( item_id.toString() == default_item_id ) + for ( int item_n = 0; item_n < m_model->packageCount(); ++item_n ) { - m_defaultModelIndex = item_idx; - break; + QModelIndex item_idx = m_model->index( item_n, 0 ); + QVariant item_id = m_model->data( item_idx, PackageListModel::IdRole ); + + if ( item_id.toString() == default_item_id ) + { + m_defaultModelIndex = item_idx; + break; + } } } } + else + { + setPackageChoice( CalamaresUtils::getString( configurationMap, "packageChoice" ) ); + } } diff --git a/src/modules/packagechooserq/packagechooserq.conf b/src/modules/packagechooserq/packagechooserq.conf index ecf9ed1a6..8fd3a08f6 100644 --- a/src/modules/packagechooserq/packagechooserq.conf +++ b/src/modules/packagechooserq/packagechooserq.conf @@ -45,6 +45,12 @@ method: legacy # # id: "" -# The *pkgc* is used for setting the default selection in the QML view -pkgc: libreoffice +# The *packageChoice* value is used for setting the default selection +# in the QML view; this should match one of the keys used in the QML +# module for package names. +# +# (e.g. the sample QML uses "no_office_suite", "minimal_install" and +# "libreoffice" as possible choices). +# +packageChoice: libreoffice diff --git a/src/modules/packagechooserq/packagechooserq.qml b/src/modules/packagechooserq/packagechooserq.qml index cf7454fe9..98f7b8038 100644 --- a/src/modules/packagechooserq/packagechooserq.qml +++ b/src/modules/packagechooserq/packagechooserq.qml @@ -84,8 +84,8 @@ Item { print("L not used") } else { - config.pkgc = "libreoffice" - print( config.pkgc ) + config.packageChoice = "libreoffice" + print( config.packageChoice ) } } } @@ -152,7 +152,7 @@ Item { } else { print("No Office Suite") - config.pkgc = "no_office_suite" + config.packageChoice = "no_office_suite" } } } @@ -220,7 +220,7 @@ Item { } else { print("minimal") - config.pkgc = "minimal_install" + config.packageChoice = "minimal_install" } } } From 33e7e8da58a24bea4ca2916c79e0454e7dfd272d Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 22:41:13 +0200 Subject: [PATCH 72/87] [packagechooser] Massage API - use updateGlobalStorage() for both single-selection and model-based approaches, although the model-based one needs extra parameters. - complain about inconsistent settings and API calls (e.g. setting a model and single-selection at the same time) --- src/modules/packagechooser/Config.cpp | 39 ++++++++++++++++++- src/modules/packagechooser/Config.h | 9 ++++- .../PackageChooserQmlViewStep.cpp | 2 +- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/modules/packagechooser/Config.cpp b/src/modules/packagechooser/Config.cpp index 11288bde6..0001d9fc6 100644 --- a/src/modules/packagechooser/Config.cpp +++ b/src/modules/packagechooser/Config.cpp @@ -98,10 +98,13 @@ Config::introductionPackage() const void Config::updateGlobalStorage( const QStringList& selected ) const { + if ( m_packageChoice.has_value() ) + { + cWarning() << "Inconsistent package choices -- both model and single-selection QML"; + } if ( m_method == PackageChooserMethod::Legacy ) { QString value = selected.join( ',' ); - // QString value = ( m_pkgc ); Calamares::JobQueue::instance()->globalStorage()->insert( m_id, value ); cDebug() << m_id << "selected" << value; } @@ -118,6 +121,36 @@ Config::updateGlobalStorage( const QStringList& selected ) const } } +void +Config::updateGlobalStorage() const +{ + if ( m_model->packageCount() > 0 ) + { + cWarning() << "Inconsistent package choices -- both model and single-selection QML"; + } + if ( m_method == PackageChooserMethod::Legacy ) + { + auto* gs = Calamares::JobQueue::instance()->globalStorage(); + if ( m_packageChoice.has_value() ) + { + gs->insert( m_id, m_packageChoice.value() ); + } + else + { + gs->remove( m_id ); + } + } + else if ( m_method == PackageChooserMethod::Packages ) + { + cWarning() << "Unsupported single-selection packagechooser method 'Packages'"; + } + else + { + cWarning() << "Unknown packagechooser method" << smash( m_method ); + } +} + + void Config::setPackageChoice( const QString& packageChoice ) { @@ -252,5 +285,9 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) else { setPackageChoice( CalamaresUtils::getString( configurationMap, "packageChoice" ) ); + if ( m_method != PackageChooserMethod::Legacy ) + { + cWarning() << "Single-selection QML module must use 'Legacy' method."; + } } } diff --git a/src/modules/packagechooser/Config.h b/src/modules/packagechooser/Config.h index 75ff0d0c6..85b8de83f 100644 --- a/src/modules/packagechooser/Config.h +++ b/src/modules/packagechooser/Config.h @@ -85,8 +85,13 @@ public: * (and only) the packages in @p selected as selected. */ void updateGlobalStorage( const QStringList& selected ) const; - /// As updateGlobalStorage() with an empty selection list - void fillGSSecondaryConfiguration() const { updateGlobalStorage( QStringList() ); } + /** @brief Write selection to global storage + * + * Updates the GS keys for this packagechooser, marking **only** + * the package choice as selected. This assumes that the single- + * selection QML code is in use. + */ + void updateGlobalStorage() const; QString packageChoice() const { return m_packageChoice.value_or( QString() ); } void setPackageChoice( const QString& packageChoice ); diff --git a/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp b/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp index 109260ca9..543c9771d 100644 --- a/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp +++ b/src/modules/packagechooserq/PackageChooserQmlViewStep.cpp @@ -74,7 +74,7 @@ PackageChooserQmlViewStep::jobs() const void PackageChooserQmlViewStep::onLeave() { - m_config->fillGSSecondaryConfiguration(); + m_config->updateGlobalStorage(); } void From 6def41fab4270498ea8df5e98fc48b950572189b Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 22:57:17 +0200 Subject: [PATCH 73/87] [packagechooserq] Remove debug-output in QML --- .../packagechooserq/packagechooserq.qml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/modules/packagechooserq/packagechooserq.qml b/src/modules/packagechooserq/packagechooserq.qml index 98f7b8038..7d1613582 100644 --- a/src/modules/packagechooserq/packagechooserq.qml +++ b/src/modules/packagechooserq/packagechooserq.qml @@ -80,12 +80,8 @@ Item { } onCheckedChanged: { - if ( ! checked ) { - print("L not used") - } - else { + if ( checked ) { config.packageChoice = "libreoffice" - print( config.packageChoice ) } } } @@ -146,12 +142,7 @@ Item { } onCheckedChanged: { - if ( ! checked ) { - print("not used") - //console.log("removed") - } - else { - print("No Office Suite") + if ( checked ) { config.packageChoice = "no_office_suite" } } @@ -215,11 +206,7 @@ Item { } onCheckedChanged: { - if ( ! checked ) { - print("M not used") - } - else { - print("minimal") + if ( checked ) { config.packageChoice = "minimal_install" } } From e311d7a89346ad61ee10a2da48cb1a7a76559725 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Fri, 3 Sep 2021 23:59:11 +0200 Subject: [PATCH 74/87] [packagechooser] Remove 'id' configuration setting - Setting 'id' (which changes the Global Storage key that gets used) is a kludge when the existing module-instance name can be used instead -- and **was** already used, as a fallback when 'id' is not set. There's no point in having two places to set a particular name. - Rip out the docs for 'id' as well. - Add documentation on the difference between single-selection (the QML implementation) and model-selection (what the Widgets version does). --- src/modules/packagechooser/Config.cpp | 35 ++++++------------- src/modules/packagechooser/Config.h | 2 -- .../packagechooser/packagechooser.conf | 12 +++---- .../packagechooserq/packagechooserq.conf | 31 ++++++++-------- 4 files changed, 29 insertions(+), 51 deletions(-) diff --git a/src/modules/packagechooser/Config.cpp b/src/modules/packagechooser/Config.cpp index 0001d9fc6..5c0db5d91 100644 --- a/src/modules/packagechooser/Config.cpp +++ b/src/modules/packagechooser/Config.cpp @@ -95,6 +95,12 @@ Config::introductionPackage() const return *defaultIntroduction; } +static inline QString +make_gs_key( const Calamares::ModuleSystem::InstanceKey& key ) +{ + return QStringLiteral( "packagechooser_" ) + key.id(); +} + void Config::updateGlobalStorage( const QStringList& selected ) const { @@ -105,8 +111,8 @@ Config::updateGlobalStorage( const QStringList& selected ) const if ( m_method == PackageChooserMethod::Legacy ) { QString value = selected.join( ',' ); - Calamares::JobQueue::instance()->globalStorage()->insert( m_id, value ); - cDebug() << m_id << "selected" << value; + Calamares::JobQueue::instance()->globalStorage()->insert( make_gs_key( m_defaultId ), value ); + cDebug() << m_defaultId << "selected" << value; } else if ( m_method == PackageChooserMethod::Packages ) { @@ -133,11 +139,11 @@ Config::updateGlobalStorage() const auto* gs = Calamares::JobQueue::instance()->globalStorage(); if ( m_packageChoice.has_value() ) { - gs->insert( m_id, m_packageChoice.value() ); + gs->insert( make_gs_key( m_defaultId ), m_packageChoice.value() ); } else { - gs->remove( m_id ); + gs->remove( make_gs_key( m_defaultId ) ); } } else if ( m_method == PackageChooserMethod::Packages ) @@ -240,26 +246,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) if ( m_method == PackageChooserMethod::Legacy ) { - const QString configId = CalamaresUtils::getString( configurationMap, "id" ); - const QString base = QStringLiteral( "packagechooser_" ); - if ( configId.isEmpty() ) - { - if ( m_defaultId.id().isEmpty() ) - { - // We got nothing to work with - m_id = base; - } - else - { - m_id = base + m_defaultId.id(); - } - cDebug() << "Using default ID" << m_id << "from" << m_defaultId.toString(); - } - else - { - m_id = base + configId; - cDebug() << "Using configured ID" << m_id; - } + cDebug() << "Using module ID" << m_defaultId; } if ( configurationMap.contains( "items" ) ) diff --git a/src/modules/packagechooser/Config.h b/src/modules/packagechooser/Config.h index 85b8de83f..32f1e8b57 100644 --- a/src/modules/packagechooser/Config.h +++ b/src/modules/packagechooser/Config.h @@ -110,8 +110,6 @@ private: PackageChooserMode m_mode = PackageChooserMode::Optional; /// How this module stores to GS PackageChooserMethod m_method = PackageChooserMethod::Legacy; - /// Id (used to identify settings from this module in GS) - QString m_id; /// Value to use for id if none is set in the config file Calamares::ModuleSystem::InstanceKey m_defaultId; /** @brief QML selection (for single-selection approaches) diff --git a/src/modules/packagechooser/packagechooser.conf b/src/modules/packagechooser/packagechooser.conf index 2bde1369c..231826cd3 100644 --- a/src/modules/packagechooser/packagechooser.conf +++ b/src/modules/packagechooser/packagechooser.conf @@ -15,11 +15,10 @@ mode: required # # - "legacy" or "custom" or "contextualprocess" # When set to "legacy", writes a GlobalStorage value for the choice that -# has been made. The key is *packagechooser_*. Normally, the module's +# has been made. The key is *packagechooser_*. The module's # instance name is used; see the *instances* section of `settings.conf`. # If there is just one packagechooser module, and no special instance is set, -# resulting GS key is probably *packagechooser@packagechooser*. -# You can set *id* to change that, but it is not recommended. +# resulting GS key is probably *packagechooser_packagechooser*. # # The GS value is a comma-separated list of the IDs of the selected # packages, or an empty string if none is selected. @@ -33,15 +32,12 @@ mode: required # consumption by the *packages* module (which should appear later # in the `exec` section. These package settings will then be handed # off to whatever package manager is configured there. -# The *id* key is not used. # # There is no need to put this module in the `exec` section. There # are no jobs that this module provides. You should put **other** # modules, either *contextualprocess* or *packages* or some custom # module, in the `exec` section to do the actual work. method: legacy -# The *id* key is used only in "legacy" mode -# id: "" # Human-visible strings in this module. These are all optional. @@ -51,13 +47,13 @@ method: legacy # Each key can have a [locale] added to it, which is used as # the translated string for that locale. For the strings # associated with the "no-selection" item, see *items*, below -# with the explicit id "". +# with the explicit item-*id* "". # labels: step: "Packages" step[nl]: "Pakketten" -# (Optional) 'id' of pre-selected list-view item. +# (Optional) item-*id* of pre-selected list-view item. # Pre-selects one of the items below. # default: kde diff --git a/src/modules/packagechooserq/packagechooserq.conf b/src/modules/packagechooserq/packagechooserq.conf index 8fd3a08f6..803c6f670 100644 --- a/src/modules/packagechooserq/packagechooserq.conf +++ b/src/modules/packagechooserq/packagechooserq.conf @@ -1,23 +1,26 @@ # SPDX-FileCopyrightText: no # SPDX-License-Identifier: CC0-1.0 # -# Configuration for the low-density software chooser ---- -# Software selection mode, to set whether the software packages -# can be chosen singly, or multiply. +# Configuration for the low-density software chooser, QML implementation +# +# The example QML implementation uses single-selection, rather than +# a model for the available packages. That makes it simpler: the +# QML itself codes the available options, descriptions and images +# -- after all, this is **low density** selection, so a custom UI +# can make sense for the few choices that need to be made. +# # -# The example QML module does not use a model, and ignores this value. -mode: required +--- # Software installation method: # # - "legacy" or "custom" or "contextualprocess" # When set to "legacy", writes a GlobalStorage value for the choice that -# has been made. The key is *packagechooser_*. Normally, the module's +# has been made. The key is *packagechooser_*. The module's # instance name is used; see the *instances* section of `settings.conf`. -# If there is just one packagechooser module, and no special instance is set, -# resulting GS key is probably *packagechooser@packagechooser*. -# You can set *id* to change that, but it is not recommended. +# If there is just one packagechooserq module, and no special instance is set, +# resulting GS key is probably *packagechooser_packagechooserq*. +# (Do note that the prefix of the GS key remains "packagechooser_") # # The GS value is a comma-separated list of the IDs of the selected # packages, or an empty string if none is selected. @@ -31,19 +34,13 @@ mode: required # consumption by the *packages* module (which should appear later # in the `exec` section. These package settings will then be handed # off to whatever package manager is configured there. -# The *id* key is not used. # # There is no need to put this module in the `exec` section. There # are no jobs that this module provides. You should put **other** # modules, either *contextualprocess* or *packages* or some custom # module, in the `exec` section to do the actual work. -method: legacy - -# The *id* key is used only in "legacy" mode and changes the GlobalStorage -# key used to store the package choices. It is not recommended to use -# this, since the module instance key does the same job. # -# id: "" +method: legacy # The *packageChoice* value is used for setting the default selection # in the QML view; this should match one of the keys used in the QML From 5a6e033e822d7c0e7ae5c59b222ec3337c2ed471 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 5 Sep 2021 13:34:38 +0200 Subject: [PATCH 75/87] [bootloader] Flags for root-on-BTRFS This is directly derived from Anke Boersma's KaOS code in module *bootldr*, which adds the same kernel parameter via a slightly different route. --- src/modules/bootloader/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/modules/bootloader/main.py b/src/modules/bootloader/main.py index 7192df75c..68cbddd0e 100644 --- a/src/modules/bootloader/main.py +++ b/src/modules/bootloader/main.py @@ -133,6 +133,12 @@ def create_systemd_boot_conf(install_path, efi_dir, uuid, entry, entry_name, ker "root=/dev/mapper/" + partition["luksMapperName"]] + # systemd-boot with a BTRFS root filesystem needs to be told + # about the root subvolume. + for partition in partitions: + if partition["mountPoint"] == "/" and partition["fs"] == "btrfs": + kernel_params.append("rootflags=subvol=@") + if cryptdevice_params: kernel_params.extend(cryptdevice_params) else: From ea32c12938fdee71e5ac77a153e119bc71de128e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 11:26:59 +0200 Subject: [PATCH 76/87] Changes: credits for recent work --- CHANGES | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 76d430a97..9826cb4d1 100644 --- a/CHANGES +++ b/CHANGES @@ -12,12 +12,15 @@ website will have to do for older versions. This release contains contributions from (alphabetically by first name): - Anke Boersma - Artem Grinev + - Nico 'dr460nf1r3' (new contributor, welcome!) - Waneon Kim (new contributor, welcome!) ## Core ## - No core changes yet ## Modules ## + - BTRFS partitions are no longer listed as "check in phase 2" in + the *fstab* module. (Thanks Nico) - The *localeq* module had the i18n.qml rewritten to make it easier to customize. A bug in the layout has been fixed, and the overall look has been updated. @@ -25,7 +28,7 @@ This release contains contributions from (alphabetically by first name): user on the installed system, so that network configuration changes made in the live system are automatically used after installation. #1755 - *partition* no longer allows you to delete an extended partition with - children (which led to crashes). Thanks Artem! #1749 + children (which led to crashes). #1749 (Thanks Artem) - *partition* complains in more detail about the state of the UEFI boot partition (under manual partitioning schemes). #1761 - *welcome* can now check multiple URLs to determine if internet connectivity @@ -38,6 +41,7 @@ This release contains contributions from (alphabetically by first name): Summaryq brings the option to present the summary page in a customizable way, with a bit more of a contemporary look. Packagechooserq adds the option to preselect an item and displays all options in one overview. + (Thanks Anke) From 07572d36c0b27607d80581abf7452a8a4a88e876 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 5 Sep 2021 14:18:52 +0200 Subject: [PATCH 77/87] Changes: pre-release housekeeping --- CHANGES | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 9826cb4d1..0571afd2f 100644 --- a/CHANGES +++ b/CHANGES @@ -7,7 +7,7 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. -# 3.2.42 (unreleased) # +# 3.2.42 (2021-09-06) # This release contains contributions from (alphabetically by first name): - Anke Boersma diff --git a/CMakeLists.txt b/CMakeLists.txt index 44a88074f..efc2f0cff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ project( CALAMARES LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development ### OPTIONS # From 9c435e0173a6d1397bd1675e0bed89b663a9c373 Mon Sep 17 00:00:00 2001 From: Calamares CI Date: Sun, 5 Sep 2021 14:20:30 +0200 Subject: [PATCH 78/87] i18n: [calamares] Automatic merge of Transifex translations --- lang/calamares_de.ts | 27 ++++++------ lang/calamares_ko.ts | 2 +- lang/calamares_lt.ts | 10 ++--- lang/calamares_ml.ts | 38 ++++++++-------- lang/calamares_pt_BR.ts | 30 ++++++------- lang/calamares_ru.ts | 56 +++++++++++------------ lang/calamares_si.ts | 98 ++++++++++++++++++++--------------------- lang/calamares_th.ts | 36 +++++++-------- lang/calamares_zh_CN.ts | 16 +++---- lang/calamares_zh_HK.ts | 6 +-- 10 files changed, 160 insertions(+), 159 deletions(-) diff --git a/lang/calamares_de.ts b/lang/calamares_de.ts index cb48adb16..e682848c5 100644 --- a/lang/calamares_de.ts +++ b/lang/calamares_de.ts @@ -510,12 +510,13 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. Set filesystem label on %1. - + Setze Dateisytem-Label für %1. Set filesystem label <strong>%1</strong> to partition <strong>%2</strong>. - + Setze Dateisytem-Label <strong>%1</strong> für Partition <strong>%2</strong>. + @@ -896,7 +897,7 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. OK! - + OK! @@ -1012,12 +1013,12 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. Label for the filesystem - + Label für das Dateisystem FS Label: - + FS Label: @@ -1376,12 +1377,12 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. Label for the filesystem - + Label für das Dateisystem FS Label: - + FS Label: @@ -1889,7 +1890,7 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. Quit - + Beenden @@ -2113,7 +2114,7 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. Select your preferred Region, or use the default settings. - + Wählen Sie Ihre bevorzugte Region oder nutzen Sie die Standardeinstellungen. @@ -2653,7 +2654,7 @@ Dies wird das Installationsprogramm beenden und alle Änderungen gehen verloren. File System Label - + Dateisystem-Label @@ -4263,7 +4264,7 @@ Ausgabe: root is not allowed as username. - + root ist als Benutzername nicht erlaubt. @@ -4283,7 +4284,7 @@ Ausgabe: localhost is not allowed as hostname. - + localhost ist als Computername nicht erlaubt. @@ -4323,7 +4324,7 @@ Ausgabe: Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. - + Es sind nur Buchstaben, Zahlen, Unterstrich und Bindestrich erlaubt, minimal zwei Zeichen. diff --git a/lang/calamares_ko.ts b/lang/calamares_ko.ts index e8fa92dd6..695ccfc42 100644 --- a/lang/calamares_ko.ts +++ b/lang/calamares_ko.ts @@ -4312,7 +4312,7 @@ Output: Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. - + 문자, 숫자, 밑줄 및 하이픈만 허용되며, 최소 2자 이상이어야 합니다. diff --git a/lang/calamares_lt.ts b/lang/calamares_lt.ts index fc2dc392d..88b7ecc97 100644 --- a/lang/calamares_lt.ts +++ b/lang/calamares_lt.ts @@ -232,7 +232,7 @@ Boost.Python error in job "%1". - Boost.Python klaida užduotyje "%1". + Boost.Python klaida užduotyje „%1“. @@ -1225,7 +1225,7 @@ Diegimo programa užbaigs darbą ir visi pakeitimai bus prarasti. The installer failed to deactivate a volume group named %1. - Diegimo programai nepavyko pasyvinti tomų grupės, pavadinimu "%1". + Diegimo programai nepavyko pasyvinti tomų grupės, pavadinimu „%1“. @@ -1309,7 +1309,7 @@ Diegimo programa užbaigs darbą ir visi pakeitimai bus prarasti. Skip writing LUKS configuration for Dracut: "/" partition is not encrypted - Praleisti LUKS konfigūracijos, kuri yra skirta Dracut, įrašymą: "/" skaidinys nėra užšifruotas + Praleisti LUKS konfigūracijos, kuri yra skirta Dracut, įrašymą: „/“ skaidinys nėra užšifruotas @@ -2162,7 +2162,7 @@ Diegimo programa užbaigs darbą ir visi pakeitimai bus prarasti. Memory allocation error when setting '%1' - Atminties paskirstymo klaida, nustatant "%1" + Atminties paskirstymo klaida, nustatant „%1“ @@ -3123,7 +3123,7 @@ Išvestis: The installer failed to remove a volume group named '%1'. - Diegimo programai nepavyko pašalinti tomų grupės, pavadinimu "%1". + Diegimo programai nepavyko pašalinti tomų grupės, pavadinimu „%1“. diff --git a/lang/calamares_ml.ts b/lang/calamares_ml.ts index 452c86b3b..f2f7a0885 100644 --- a/lang/calamares_ml.ts +++ b/lang/calamares_ml.ts @@ -6,7 +6,7 @@ Manage auto-mount settings - + ഓട്ടോ-മൗണ്ട് ക്രമീകരണങ്ങൾ സജ്ജീകരിക്കുക @@ -4024,7 +4024,7 @@ Output: Back - + പുറകോട്ട് @@ -4082,7 +4082,7 @@ Output: Back - + പുറകോട്ട് @@ -4175,7 +4175,7 @@ Output: Back - + പുറകോട്ട് @@ -4203,7 +4203,7 @@ Output: Login Name - + പ്രവേശന നാമം @@ -4238,7 +4238,7 @@ Output: localhost is not allowed as hostname. - + localhost അനുവദനീയമായ ഒരു ഹോസ്റ്റ്‌നെയിം അല്ല. @@ -4263,7 +4263,7 @@ Output: Validate passwords quality - + രഹസ്യവാക്കിന്റെ ഗുണനിലവാരം ഉറപ്പുവരുത്തുക @@ -4273,17 +4273,17 @@ Output: Log in automatically without asking for the password - + രഹസ്യവാക്ക് ചോദിക്കാതെ സ്വയം പ്രവേശിക്കുക Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. - + അക്ഷരങ്ങൾ, അക്കങ്ങൾ, ഹൈഫൻ, അണ്ടർസ്കോർ എന്നിവ മാത്രമേ അനുവദിക്കപ്പെട്ടിട്ടുള്ളൂ, കുറഞ്ഞത് രണ്ടെണ്ണമെങ്കിലും. Reuse user password as root password - + ഉപയോക്തൃ രഹസ്യവാക്ക് റൂട്ട് രഹസ്യവാക്കായി പുനരുപയോഗിക്കുക @@ -4293,22 +4293,22 @@ Output: Choose a root password to keep your account safe. - + താങ്കളുടെ അക്കൗണ്ട് സുരക്ഷിതമാക്കാൻ ഒരു റൂട്ട് രഹസ്യവാക്ക് തിരഞ്ഞെടുക്കുക. Root Password - + റൂട്ട് രഹസ്യവാക്ക് Repeat Root Password - + റൂട്ട് രഹസ്യവാക്ക് വീണ്ടും Enter the same password twice, so that it can be checked for typing errors. - + ടൈപ്പിങ്ങ് പിഴവുകളില്ല എന്നുറപ്പിക്കുന്നതിനായി ഒരേ രഹസ്യവാക്ക് രണ്ട് തവണ നൽകുക. @@ -4322,27 +4322,27 @@ Output: About - + വിവരം Support - + സഹായം Known issues - + അറിയാവുന്ന പ്രശ്നങ്ങൾ Release notes - + പ്രകാശനക്കുറിപ്പുകൾ Donate - + സംഭാവന diff --git a/lang/calamares_pt_BR.ts b/lang/calamares_pt_BR.ts index 039b689ae..4bf0ac14d 100644 --- a/lang/calamares_pt_BR.ts +++ b/lang/calamares_pt_BR.ts @@ -24,7 +24,7 @@ This system was started with a <strong>BIOS</strong> boot environment.<br><br>To configure startup from a BIOS environment, this installer must install a boot loader, like <strong>GRUB</strong>, either at the beginning of a partition or on the <strong>Master Boot Record</strong> near the beginning of the partition table (preferred). This is automatic, unless you choose manual partitioning, in which case you must set it up on your own. - Este sistema foi iniciado utilizando o <strong>BIOS</strong> como ambiente de inicialização.<br><br>Para configurar a inicialização em um ambiente BIOS, este instalador deve instalar um gerenciador de boot, como o <strong>GRUB</strong>, no começo de uma partição ou no <strong>Master Boot Record</strong>, perto do começo da tabela de partições (recomendado). Esse processo é automático, a não ser que você escolha o particionamento manual, onde você deverá configurá-lo manualmente. + Este sistema foi iniciado utilizando o <strong>BIOS</strong> como ambiente de inicialização.<br><br>Para configurar a inicialização em um ambiente BIOS, este instalador deve instalar um gerenciador de inicialização, como o <strong>GRUB</strong>, no começo de uma partição ou no <strong>Master Boot Record</strong>, perto do começo da tabela de partições (recomendado). Esse processo é automático, a não ser que você escolha o particionamento manual, onde você deverá configurá-lo manualmente. @@ -510,12 +510,12 @@ O instalador será fechado e todas as alterações serão perdidas. Set filesystem label on %1. - + Definir etiqueta do sistema de arquivos em %1. Set filesystem label <strong>%1</strong> to partition <strong>%2</strong>. - + Definir etiqueta do sistema de arquivos <strong>%1</strong> para partição <strong>%2</strong>. @@ -896,7 +896,7 @@ O instalador será fechado e todas as alterações serão perdidas. OK! - + OK! @@ -1012,12 +1012,12 @@ O instalador será fechado e todas as alterações serão perdidas. Label for the filesystem - + Etiqueta para o sistema de arquivos FS Label: - + Etiqueta do FS: @@ -1376,12 +1376,12 @@ O instalador será fechado e todas as alterações serão perdidas. Label for the filesystem - + Etiqueta para o sistema de arquivos FS Label: - + Etiqueta do FS: @@ -1889,7 +1889,7 @@ O instalador será fechado e todas as alterações serão perdidas. Quit - + Sair @@ -2113,7 +2113,7 @@ O instalador será fechado e todas as alterações serão perdidas. Select your preferred Region, or use the default settings. - + Selecione sua região preferida ou use as configurações predefinidas. @@ -2653,7 +2653,7 @@ O instalador será fechado e todas as alterações serão perdidas. File System Label - + Etiqueta do Sistema de Arquivos @@ -2844,7 +2844,7 @@ O instalador será fechado e todas as alterações serão perdidas. Boot partition not encrypted - Partição de boot não criptografada + Partição de inicialização não criptografada @@ -4263,7 +4263,7 @@ Saída: root is not allowed as username. - + root não é permitido como um nome de usuário. @@ -4283,7 +4283,7 @@ Saída: localhost is not allowed as hostname. - + localhost não é permitido como hostname. @@ -4323,7 +4323,7 @@ Saída: Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. - + São permitidos apenas letras, números, sublinhado e hífen, com no mínimo dois caracteres. diff --git a/lang/calamares_ru.ts b/lang/calamares_ru.ts index 113a679d5..a1d34b9bb 100644 --- a/lang/calamares_ru.ts +++ b/lang/calamares_ru.ts @@ -277,7 +277,7 @@ (% секунда) (% секунд) (% секунд) - (% секунд) + (%n секунд) @@ -1011,12 +1011,12 @@ The installer will quit and all changes will be lost. Label for the filesystem - + Метка файловой системы FS Label: - + Метка ФС: @@ -1054,7 +1054,7 @@ The installer will quit and all changes will be lost. Create new %1MiB partition on %3 (%2). - + Создать новый раздел %1МиБ на %3 (%2). @@ -1069,7 +1069,7 @@ The installer will quit and all changes will be lost. Create new <strong>%1MiB</strong> partition on <strong>%3</strong> (%2). - + Создать новый раздел <strong>%1МиБ</strong> на <strong>%3</strong> (%2). @@ -1375,12 +1375,12 @@ The installer will quit and all changes will be lost. Label for the filesystem - + Метка файловой системы FS Label: - + Метка ФС: @@ -1442,7 +1442,7 @@ The installer will quit and all changes will be lost. Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong>%3. - + Настроить <strong>новый</strong> %2 раздел с точкой монтирования <strong>%1</strong> %3. @@ -1457,7 +1457,7 @@ The installer will quit and all changes will be lost. Set up %3 partition <strong>%1</strong> with mount point <strong>%2</strong>%4. - + Настроить %3 раздел <strong>%1</strong> с точкой монтирования <strong>%2</strong>%4. @@ -2525,7 +2525,7 @@ The installer will quit and all changes will be lost. login - + Вход @@ -2668,7 +2668,7 @@ The installer will quit and all changes will be lost. File System Label - + Метка файловой системы @@ -3053,7 +3053,7 @@ Output: Directory not found - + Папка не найдена @@ -3088,7 +3088,7 @@ Output: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Этот компьютер соответствует не всем рекомендуемым требованиям для установки %1.<br/>Можно продолжить установку, но некоторые возможности могут быть недоступны.</p> @@ -3206,7 +3206,7 @@ Output: <p>This computer does not satisfy some of the recommended requirements for setting up %1.<br/> Setup can continue, but some features might be disabled.</p> - + <p>Этот компьютер соответствует не всем рекомендуемым требованиям для установки %1.<br/>Можно продолжить установку, но некоторые возможности могут быть недоступны.</p> @@ -3657,7 +3657,7 @@ Output: %L1 / %L2 slide counter, %1 of %2 (numeric) - + %L1 / %L2 @@ -3714,13 +3714,13 @@ Output: Configuring KDE user feedback. - + Настройка обратной связи KDE Error in KDE user feedback configuration. - + Ошибка в настройке обратной связи KDE @@ -3936,7 +3936,7 @@ Output: &Donate - + Пожертвовать @@ -4062,7 +4062,7 @@ Output: Installation Completed - + Установка завершена @@ -4073,7 +4073,7 @@ Output: Close Installer - + Закрыть установщик @@ -4117,7 +4117,7 @@ Output: Layouts - + Раскладки @@ -4127,7 +4127,7 @@ Output: Click your preferred keyboard model to select layout and variant, or use the default one based on the detected hardware. - + Выберете предпочитаемую модель клавиатуры, чтобы выбрать раскладку и вариант, или используйте модель по умолчанию в зависимости от обнаруженного оборудования. @@ -4231,7 +4231,7 @@ Output: If more than one person will use this computer, you can create multiple accounts after installation. - + Если этот компьютер используется несколькими людьми, Вы сможете создать соответствующие учетные записи сразу после установки. @@ -4241,7 +4241,7 @@ Output: root is not allowed as username. - + root не допускается в качестве имени пользователя. @@ -4261,7 +4261,7 @@ Output: localhost is not allowed as hostname. - + localhost не допускается в качестве имени пользователя. @@ -4296,7 +4296,7 @@ Output: Log in automatically without asking for the password - + Входить автоматически, не спрашивая пароль @@ -4326,12 +4326,12 @@ Output: Repeat Root Password - + Повторите пароль от root Enter the same password twice, so that it can be checked for typing errors. - + Введите пароль повторно, чтобы не допустить ошибок при вводе diff --git a/lang/calamares_si.ts b/lang/calamares_si.ts index 0b46d0858..7783b17b1 100644 --- a/lang/calamares_si.ts +++ b/lang/calamares_si.ts @@ -73,7 +73,7 @@ GlobalStorage - + ගෝලීය ආචයනය @@ -88,7 +88,7 @@ Type: - + වර්ගය: @@ -99,7 +99,7 @@ Interface: - + අතුරුමුහුණත: @@ -240,7 +240,7 @@ Loading ... - + පූරණය වෙමින්... @@ -308,18 +308,18 @@ &Yes - + ඔව් (Y) &No - + නැහැ (N) &Close - + වසන්න (C) @@ -388,7 +388,7 @@ Link copied to clipboard Go &back - + ආපසු යන්න @@ -428,7 +428,7 @@ Link copied to clipboard &Back - + ආපසු (B) @@ -448,7 +448,7 @@ Link copied to clipboard Cancel installation? - + ස්ථාපනය අවලංගු කරනවාද? @@ -543,7 +543,7 @@ The installer will quit and all changes will be lost. Current: - + වත්මන්: @@ -845,7 +845,7 @@ The installer will quit and all changes will be lost. Your username is too long. - + පරිශීලක නාමය දිග වැඩිය. @@ -890,7 +890,7 @@ The installer will quit and all changes will be lost. OK! - + හරි! @@ -961,7 +961,7 @@ The installer will quit and all changes will be lost. Si&ze: - + ප්‍රමාණය: @@ -986,7 +986,7 @@ The installer will quit and all changes will be lost. Fi&le System: - + ගොනු පද්ධතිය: @@ -1139,12 +1139,12 @@ The installer will quit and all changes will be lost. Create user %1 - + %1 පරිශීලක සාදන්න Create user <strong>%1</strong>. - + <strong>%1</strong> පරිශීලක සාදන්න. @@ -1155,12 +1155,12 @@ The installer will quit and all changes will be lost. Creating user %1 - + %1 පරිශීලක සෑදෙමින් Configuring user %1 - + %1 පරිශීලක වින්‍යාසගත වෙමින් @@ -1280,13 +1280,13 @@ The installer will quit and all changes will be lost. %1 - %2 (%3) device[name] - size[number] (device-node[name]) - + %1 - %2 (%3) %1 - (%2) device[name] - (device-node[name]) - + %1 - (%2) @@ -1350,7 +1350,7 @@ The installer will quit and all changes will be lost. Si&ze: - + ප්‍රමාණය: @@ -1360,7 +1360,7 @@ The installer will quit and all changes will be lost. Fi&le System: - + ගොනු පද්ධතිය: @@ -1706,7 +1706,7 @@ The installer will quit and all changes will be lost. Keyboard - + යතුරුපුවරුව @@ -1714,7 +1714,7 @@ The installer will quit and all changes will be lost. Keyboard - + යතුරුපුවරුව @@ -1788,7 +1788,7 @@ The installer will quit and all changes will be lost. License - + බලපත්‍රය @@ -1796,7 +1796,7 @@ The installer will quit and all changes will be lost. URL: %1 - + ඒ.ස.නි.: %1 @@ -1833,7 +1833,7 @@ The installer will quit and all changes will be lost. File: %1 - + ගොනුව: %1 @@ -1867,7 +1867,7 @@ The installer will quit and all changes will be lost. &Change... - + වෙනස් කරන්න... @@ -1875,7 +1875,7 @@ The installer will quit and all changes will be lost. Location - + ස්ථානය @@ -1883,7 +1883,7 @@ The installer will quit and all changes will be lost. Quit - + ඉවත් වන්න @@ -1891,7 +1891,7 @@ The installer will quit and all changes will be lost. Location - + ස්ථානය @@ -1973,27 +1973,27 @@ The installer will quit and all changes will be lost. Office software - + කාර්යාලීය මෘදුකාංගය Office package - + කාර්යාලීය ඇසුරුම Browser software - + අතිරික්සු මෘදුකාංගය Browser package - + අතිරික්සු ඇසුරුම Web browser - + වියමන අතිරික්සුව @@ -2003,7 +2003,7 @@ The installer will quit and all changes will be lost. Services - + සේවා @@ -2018,32 +2018,32 @@ The installer will quit and all changes will be lost. Applications - + යෙදුම් Communication - + සන්නිවේදනය Development - + සංවර්ධනය Office - + කාර්යාලීය Multimedia - + බහුමාධ්‍ය Internet - + අන්තර්ජාලය @@ -2066,7 +2066,7 @@ The installer will quit and all changes will be lost. Notes - + සටහන් @@ -2135,12 +2135,12 @@ The installer will quit and all changes will be lost. Password is too short - + මුරපදය ඉතා කෙටිය Password is too long - + මුරපදය ඉතා දිගය @@ -2223,7 +2223,7 @@ The installer will quit and all changes will be lost. The password is too short - + මුරපදය ඉතා කෙටිය @@ -2796,7 +2796,7 @@ The installer will quit and all changes will be lost. Current: - + වත්මන්: diff --git a/lang/calamares_th.ts b/lang/calamares_th.ts index 27e961293..4525b3415 100644 --- a/lang/calamares_th.ts +++ b/lang/calamares_th.ts @@ -285,7 +285,7 @@ Setup Failed - + การตั้งค่าล้มเหลว @@ -306,18 +306,18 @@ &Yes - + &ใช่ &No - + &ไม่ &Close - + ปิ&ด @@ -376,7 +376,7 @@ Link copied to clipboard &Set up now - &ตั้งค่าตอนนี้ + ตั้&งค่าตอนนี้ @@ -391,12 +391,12 @@ Link copied to clipboard &Set up - &ตั้งค่า + ตั้&งค่า &Install - &ติดตั้ง + ติ&ดตั้ง @@ -534,7 +534,7 @@ The installer will quit and all changes will be lost. Select storage de&vice: - + เลือกอุปก&รณ์จัดเก็บ: @@ -562,7 +562,7 @@ The installer will quit and all changes will be lost. <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + <strong>เลือกพาร์ทิชันที่จะลดขนาด แล้วลากแถบด้านล่างเพื่อปรับขนาด</strong> @@ -597,7 +597,7 @@ The installer will quit and all changes will be lost. This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - ดูเหมือนว่าอุปกรณ์จัดเก็บนี้ไม่มีระบบปฏิบัติการ คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บของคุณ + ดูเหมือนว่าอุปกรณ์จัดเก็บข้อมูลนี้ไม่มีระบบปฏิบัติการ คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บข้อมูลของคุณ @@ -605,7 +605,7 @@ The installer will quit and all changes will be lost. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - + <strong>ล้างดิสก์</strong><br/>การกระทำนี้จะ<font color="red">ลบ</font>ข้อมูลทั้งหมดที่อยู่บนอุปกรณ์จัดเก็บข้อมูลที่เลือก @@ -626,17 +626,17 @@ The installer will quit and all changes will be lost. This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - อุปกรณ์จัดเก็บนี้มีระบบปฏิบัติการ %1 อยู่ คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บของคุณ + อุปกรณ์จัดเก็บข้อมูลนี้มีระบบปฏิบัติการ %1 อยู่ คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บข้อมูลของคุณ This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - อุปกรณ์จัดเก็บนี้มีระบบปฏิบัติการอยู่แล้ว คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บของคุณ + อุปกรณ์จัดเก็บข้อมูลนี้มีระบบปฏิบัติการอยู่แล้ว คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บข้อมูลของคุณ This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - อุปกรณ์จัดเก็บนี้มีหลายระบบปฏิบัติการ คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บของคุณ + อุปกรณ์จัดเก็บข้อมูลนี้มีหลายระบบปฏิบัติการ คุณต้องการทำอย่างไร?<br/>คุณจะสามารถทบทวนและยืนยันตัวเลือกของคุณก่อนที่จะกระทำการเปลี่ยนแปลงไปยังอุปกรณ์จัดเก็บข้อมูลของคุณ @@ -819,7 +819,7 @@ The installer will quit and all changes will be lost. This program will ask you some questions and set up %2 on your computer. - โปรแกรมนี้จะถามตำถามต่าง ๆ เพื่อติดตั้ง %2 ลงในคอมพิวเตอร์ของคุณ + โปรแกรมนี้จะถามคำถามต่าง ๆ เพื่อติดตั้ง %2 ลงในคอมพิวเตอร์ของคุณ @@ -894,7 +894,7 @@ The installer will quit and all changes will be lost. Setup Failed - + การตั้งค่าล้มเหลว @@ -3329,7 +3329,7 @@ Output: This program will ask you some questions and set up %2 on your computer. - โปรแกรมนี้จะถามคุณบางอย่าง เพื่อติดตั้ง %2 ไว้ในคอมพิวเตอร์ของคุณ + โปรแกรมนี้จะถามคำถามต่าง ๆ เพื่อติดตั้ง %2 ลงในคอมพิวเตอร์ของคุณ @@ -3889,7 +3889,7 @@ Output: &About - &A เกี่ยวกับ + &เกี่ยวกับ diff --git a/lang/calamares_zh_CN.ts b/lang/calamares_zh_CN.ts index a98615982..a2a9757eb 100644 --- a/lang/calamares_zh_CN.ts +++ b/lang/calamares_zh_CN.ts @@ -105,7 +105,7 @@ Crashes Calamares, so that Dr. Konqui can look at it. - + 使 Calamares 崩溃,以便 Konqui 医生可以查看它。 @@ -130,7 +130,7 @@ Displays the tree of widget names in the log (for stylesheet debugging). - + 在日志中显示小部件名称树(用于样式表调试)。 @@ -514,7 +514,7 @@ The installer will quit and all changes will be lost. Set filesystem label <strong>%1</strong> to partition <strong>%2</strong>. - + 设置文件系统标签 <strong>%1</strong> 至分区 <strong>%2</strong>。 @@ -1056,7 +1056,7 @@ The installer will quit and all changes will be lost. Create new %1MiB partition on %3 (%2). - + 在 %3 (%2) 上建立新的 %1MiB 分区。 @@ -1071,7 +1071,7 @@ The installer will quit and all changes will be lost. Create new <strong>%1MiB</strong> partition on <strong>%3</strong> (%2). - + 在<strong>%3</strong>(%2)上创建新的<strong>%1MiB</strong>分区 @@ -1445,12 +1445,12 @@ The installer will quit and all changes will be lost. Set up <strong>new</strong> %2 partition with mount point <strong>%1</strong>%3. - + 设置 <strong>新的</strong> 含挂载点 <strong>%1</strong>%3 的 %2 分区。 Install %2 on %3 system partition <strong>%1</strong> with features <em>%4</em>. - + 在具有功能<em>%4</em>的 %3 系统分区<strong>%1</strong>上安装 %2。 @@ -4319,7 +4319,7 @@ Output: Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. - + 只允许字母、数组、下划线"_" 和 连字符"-",最少两个字符。 diff --git a/lang/calamares_zh_HK.ts b/lang/calamares_zh_HK.ts index 6c592a6d3..db1ecd4d9 100644 --- a/lang/calamares_zh_HK.ts +++ b/lang/calamares_zh_HK.ts @@ -6,7 +6,7 @@ Manage auto-mount settings - + 管理自動掛載設定 @@ -14,12 +14,12 @@ The <strong>boot environment</strong> of this system.<br><br>Older x86 systems only support <strong>BIOS</strong>.<br>Modern systems usually use <strong>EFI</strong>, but may also show up as BIOS if started in compatibility mode. - + 這個系統的<strong>開機環境</strong>。<br><br>較舊的 x86 系統只支援 <strong>BIOS</strong>。<br>現時的系統則通常使用 <strong>EFI</strong>,但若使用相容模式 (CSM),也可能顯示為 BIOS。 This system was started with an <strong>EFI</strong> boot environment.<br><br>To configure startup from an EFI environment, this installer must deploy a boot loader application, like <strong>GRUB</strong> or <strong>systemd-boot</strong> on an <strong>EFI System Partition</strong>. This is automatic, unless you choose manual partitioning, in which case you must choose it or create it on your own. - + 這個系統以 <strong>EFI</strong> 開機。<br><br>要從 EFI 環境開機,本安裝程式必須安裝開機載入器程式,像是 <strong>GRUB</strong> 或 <strong>systemd-boot</strong> 在 <strong>EFI 系統分割區</strong>。這是自動的,除非選擇手動分割;在這種情況,您必須自行選取或建立它。 From 37bdfb9faa69115ddb8964037cc3e241b0171eb2 Mon Sep 17 00:00:00 2001 From: Calamares CI Date: Sun, 5 Sep 2021 14:20:31 +0200 Subject: [PATCH 79/87] i18n: [python] Automatic merge of Transifex translations --- lang/python/az/LC_MESSAGES/python.po | 4 ++-- lang/python/az_AZ/LC_MESSAGES/python.po | 4 ++-- lang/python/cs_CZ/LC_MESSAGES/python.po | 2 +- lang/python/de/LC_MESSAGES/python.po | 18 ++++++++++++++---- lang/python/es/LC_MESSAGES/python.po | 2 +- lang/python/ko/LC_MESSAGES/python.po | 1 + lang/python/lt/LC_MESSAGES/python.po | 2 +- lang/python/pt_BR/LC_MESSAGES/python.po | 20 +++++++++++++++----- lang/python/si/LC_MESSAGES/python.po | 20 ++++++++++++-------- 9 files changed, 49 insertions(+), 24 deletions(-) diff --git a/lang/python/az/LC_MESSAGES/python.po b/lang/python/az/LC_MESSAGES/python.po index d47e30acd..f3f3693a4 100644 --- a/lang/python/az/LC_MESSAGES/python.po +++ b/lang/python/az/LC_MESSAGES/python.po @@ -4,7 +4,7 @@ # FIRST AUTHOR , YEAR. # # Translators: -# Xəyyam Qocayev , 2021 +# xxmn77 , 2021 # #, fuzzy msgid "" @@ -13,7 +13,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-07-14 12:55+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: Xəyyam Qocayev , 2021\n" +"Last-Translator: xxmn77 , 2021\n" "Language-Team: Azerbaijani (https://www.transifex.com/calamares/teams/20061/az/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/lang/python/az_AZ/LC_MESSAGES/python.po b/lang/python/az_AZ/LC_MESSAGES/python.po index d1e805fac..ba5c1f5d2 100644 --- a/lang/python/az_AZ/LC_MESSAGES/python.po +++ b/lang/python/az_AZ/LC_MESSAGES/python.po @@ -4,7 +4,7 @@ # FIRST AUTHOR , YEAR. # # Translators: -# Xəyyam Qocayev , 2021 +# xxmn77 , 2021 # #, fuzzy msgid "" @@ -13,7 +13,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-07-14 12:55+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: Xəyyam Qocayev , 2021\n" +"Last-Translator: xxmn77 , 2021\n" "Language-Team: Azerbaijani (Azerbaijan) (https://www.transifex.com/calamares/teams/20061/az_AZ/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/lang/python/cs_CZ/LC_MESSAGES/python.po b/lang/python/cs_CZ/LC_MESSAGES/python.po index 50002805d..9ac7c6b82 100644 --- a/lang/python/cs_CZ/LC_MESSAGES/python.po +++ b/lang/python/cs_CZ/LC_MESSAGES/python.po @@ -5,7 +5,7 @@ # # Translators: # pavelrz, 2017 -# LiberteCzech , 2020 +# LiberteCzech , 2020 # Pavel Borecki , 2020 # #, fuzzy diff --git a/lang/python/de/LC_MESSAGES/python.po b/lang/python/de/LC_MESSAGES/python.po index 555d4636f..b1e0beed9 100644 --- a/lang/python/de/LC_MESSAGES/python.po +++ b/lang/python/de/LC_MESSAGES/python.po @@ -5,8 +5,8 @@ # # Translators: # Adriaan de Groot , 2020 -# Gustav Gyges, 2020 # Andreas Eitel , 2020 +# Gustav Gyges, 2021 # #, fuzzy msgid "" @@ -15,7 +15,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-07-14 12:55+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: Andreas Eitel , 2020\n" +"Last-Translator: Gustav Gyges, 2021\n" "Language-Team: German (https://www.transifex.com/calamares/teams/20061/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -319,25 +319,31 @@ msgstr[1] "Entferne %(num)d Pakete." #: src/modules/packages/main.py:588 src/modules/packages/main.py:600 #: src/modules/packages/main.py:628 msgid "Package Manager error" -msgstr "" +msgstr "Fehler im Paketmanager" #: src/modules/packages/main.py:589 msgid "" "The package manager could not prepare updates. The command
    {!s}
    " "returned error code {!s}." msgstr "" +"Der Paketmanager konnte die Aktualisierungen nicht vorbereiten. Der Befehl " +"
    {!s}
    erzeugte Fehlercode {!s}." #: src/modules/packages/main.py:601 msgid "" "The package manager could not update the system. The command
    {!s}
    " " returned error code {!s}." msgstr "" +"Der Paketmanager konnte das System nicht aktualisieren. Der Befehl " +"
    {!s}
    erzeugte Fehlercode {!s}." #: src/modules/packages/main.py:629 msgid "" "The package manager could not make changes to the installed system. The " "command
    {!s}
    returned error code {!s}." msgstr "" +"Der Paketmanager konnte das installierte System nicht verändern. Der Befehl " +"
    {!s}
    erzeugte Fehlercode {!s}." #: src/modules/bootloader/main.py:43 msgid "Install bootloader." @@ -345,13 +351,15 @@ msgstr "Installiere Bootloader." #: src/modules/bootloader/main.py:502 msgid "Bootloader installation error" -msgstr "" +msgstr "Fehler beim Installieren des Bootloaders" #: src/modules/bootloader/main.py:503 msgid "" "The bootloader could not be installed. The installation command " "
    {!s}
    returned error code {!s}." msgstr "" +"Der Bootloader konnte nicht installiert werden. Der Installationsbefehl " +"
    {!s}
    erzeugte Fehlercode {!s}." #: src/modules/hwclock/main.py:26 msgid "Setting hardware clock." @@ -392,6 +400,8 @@ msgstr "Schreibe fstab." #: src/modules/fstab/main.py:389 msgid "No
    {!s}
    configuration is given for
    {!s}
    to use." msgstr "" +"Keine
    {!s}
    Konfiguration gegeben die
    {!s}
    benutzen " +"könnte." #: src/modules/dummypython/main.py:35 msgid "Dummy python job." diff --git a/lang/python/es/LC_MESSAGES/python.po b/lang/python/es/LC_MESSAGES/python.po index 960e280c4..f1fe11a61 100644 --- a/lang/python/es/LC_MESSAGES/python.po +++ b/lang/python/es/LC_MESSAGES/python.po @@ -7,7 +7,7 @@ # strel, 2017 # Francisco Sánchez López de Lerma , 2018 # Guido Grasso , 2018 -# Adolfo Jayme Barrientos, 2019 +# Adolfo Jayme-Barrientos, 2019 # Miguel Mayol , 2020 # Pier Jose Gotta Perez , 2020 # diff --git a/lang/python/ko/LC_MESSAGES/python.po b/lang/python/ko/LC_MESSAGES/python.po index e6f1f1c8b..6d3f2b5f5 100644 --- a/lang/python/ko/LC_MESSAGES/python.po +++ b/lang/python/ko/LC_MESSAGES/python.po @@ -320,6 +320,7 @@ msgid "" "The package manager could not make changes to the installed system. The " "command
    {!s}
    returned error code {!s}." msgstr "" +"패키지 관리자가 설치된 시스템을 변경할 수 없습니다.
    {!s}
    명령에서 {!s} 오류 코드를 반환했습니다." #: src/modules/bootloader/main.py:43 msgid "Install bootloader." diff --git a/lang/python/lt/LC_MESSAGES/python.po b/lang/python/lt/LC_MESSAGES/python.po index 6ceb39a27..d1c10622e 100644 --- a/lang/python/lt/LC_MESSAGES/python.po +++ b/lang/python/lt/LC_MESSAGES/python.po @@ -137,7 +137,7 @@ msgstr "Bloga unsquash konfigūracija" #: src/modules/unpackfs/main.py:455 msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel" -msgstr "Jūsų branduolys nepalaiko failų sistemos, kuri skirta \"{}\" ({})" +msgstr "Jūsų branduolys nepalaiko failų sistemos, kuri skirta „{}“ ({})" #: src/modules/unpackfs/main.py:459 msgid "The source filesystem \"{}\" does not exist" diff --git a/lang/python/pt_BR/LC_MESSAGES/python.po b/lang/python/pt_BR/LC_MESSAGES/python.po index 8a9f235b4..3ceeeb09a 100644 --- a/lang/python/pt_BR/LC_MESSAGES/python.po +++ b/lang/python/pt_BR/LC_MESSAGES/python.po @@ -5,7 +5,7 @@ # # Translators: # André Marcelo Alvarenga , 2020 -# Guilherme Marçal Silva, 2020 +# Guilherme Marçal Silva, 2021 # #, fuzzy msgid "" @@ -14,7 +14,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-07-14 12:55+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" -"Last-Translator: Guilherme Marçal Silva, 2020\n" +"Last-Translator: Guilherme Marçal Silva, 2021\n" "Language-Team: Portuguese (Brazil) (https://www.transifex.com/calamares/teams/20061/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -317,39 +317,47 @@ msgstr[1] "Removendo %(num)d pacotes." #: src/modules/packages/main.py:588 src/modules/packages/main.py:600 #: src/modules/packages/main.py:628 msgid "Package Manager error" -msgstr "" +msgstr "Erro do Gerenciador de Pacotes" #: src/modules/packages/main.py:589 msgid "" "The package manager could not prepare updates. The command
    {!s}
    " "returned error code {!s}." msgstr "" +"O gerenciador de pacotes não pôde preparar as atualizações. O comando " +"
    {!s}
    retornou o código de erro {!s}." #: src/modules/packages/main.py:601 msgid "" "The package manager could not update the system. The command
    {!s}
    " " returned error code {!s}." msgstr "" +"O gerenciador de pacotes não pôde atualizar o sistema. O comando " +"
    {!s}
    retornou o código de erro {!s}." #: src/modules/packages/main.py:629 msgid "" "The package manager could not make changes to the installed system. The " "command
    {!s}
    returned error code {!s}." msgstr "" +"O gerenciador de pacotes não pôde fazer mudanças no sistema instalado. O " +"comando
    {!s}
    retornou o código de erro {!s}." #: src/modules/bootloader/main.py:43 msgid "Install bootloader." -msgstr "Instalar bootloader." +msgstr "Instalar carregador de inicialização." #: src/modules/bootloader/main.py:502 msgid "Bootloader installation error" -msgstr "" +msgstr "Erro de instalação do carregador de inicialização" #: src/modules/bootloader/main.py:503 msgid "" "The bootloader could not be installed. The installation command " "
    {!s}
    returned error code {!s}." msgstr "" +"O carregador de inicialização não pôde ser instalado. O comando de " +"instalação
    {!s}
    retornou o código de erro {!s}." #: src/modules/hwclock/main.py:26 msgid "Setting hardware clock." @@ -390,6 +398,8 @@ msgstr "Escrevendo fstab." #: src/modules/fstab/main.py:389 msgid "No
    {!s}
    configuration is given for
    {!s}
    to use." msgstr "" +"Nenhuma configuração
    {!s}
    é dada para que
    {!s}
    possa " +"utilizar." #: src/modules/dummypython/main.py:35 msgid "Dummy python job." diff --git a/lang/python/si/LC_MESSAGES/python.po b/lang/python/si/LC_MESSAGES/python.po index dc92f2203..bfe7ba323 100644 --- a/lang/python/si/LC_MESSAGES/python.po +++ b/lang/python/si/LC_MESSAGES/python.po @@ -3,6 +3,9 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # +# Translators: +# Hela Basa, 2021 +# #, fuzzy msgid "" msgstr "" @@ -10,6 +13,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-07-14 12:55+0200\n" "PO-Revision-Date: 2017-08-09 10:34+0000\n" +"Last-Translator: Hela Basa, 2021\n" "Language-Team: Sinhala (https://www.transifex.com/calamares/teams/20061/si/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -216,7 +220,7 @@ msgstr "" #: src/modules/rawfs/main.py:26 msgid "Installing data." -msgstr "" +msgstr "දත්ත ස්ථාපනය වෙමින්." #: src/modules/services-openrc/main.py:29 msgid "Configure OpenRC services" @@ -268,7 +272,7 @@ msgstr "" #: src/modules/packages/main.py:50 src/modules/packages/main.py:59 #: src/modules/packages/main.py:69 msgid "Install packages." -msgstr "" +msgstr "ඇසුරුම් ස්ථාපනය කරන්න." #: src/modules/packages/main.py:57 #, python-format @@ -279,15 +283,15 @@ msgstr "" #, python-format msgid "Installing one package." msgid_plural "Installing %(num)d packages." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "ඇසුරුමක් ස්ථාපනය වෙමින්." +msgstr[1] "ඇසුරුම් %(num)d ක් ස්ථාපනය වෙමින්." #: src/modules/packages/main.py:65 #, python-format msgid "Removing one package." msgid_plural "Removing %(num)d packages." -msgstr[0] "" -msgstr[1] "" +msgstr[0] "ඇසුරුමක් ඉවත් වෙමින්." +msgstr[1] "ඇසුරුම් %(num)d ක් ඉවත් වෙමින්." #: src/modules/packages/main.py:588 src/modules/packages/main.py:600 #: src/modules/packages/main.py:628 @@ -328,7 +332,7 @@ msgstr "" #: src/modules/hwclock/main.py:26 msgid "Setting hardware clock." -msgstr "" +msgstr "දෘඩාංග ඔරලෝසුව සැකසෙමින්." #: src/modules/mkinitfs/main.py:27 msgid "Creating initramfs with mkinitfs." @@ -381,4 +385,4 @@ msgstr "" #: src/modules/networkcfg/main.py:28 msgid "Saving network configuration." -msgstr "" +msgstr "ජාල වින්‍යාසය සුරැකෙමින්." From a1fce99a05a37eb87e207ae0b26d8f7b1317b4c0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 11:39:01 +0200 Subject: [PATCH 80/87] i18n: update language lists Keep zh_HK in 'ok' even if technically it doesn't qualify. --- CMakeLists.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index efc2f0cff..f60301ee5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,13 +133,14 @@ set( CALAMARES_DESCRIPTION_SUMMARY # `txstats.py -e`. See also # # Total 79 languages -set( _tx_complete az az_AZ ca fi_FI he hi hr ja lt sq sv uk zh_TW ) -set( _tx_good as be ca@valencia cs_CZ da de fr fur it_IT ko ml nl - pt_BR pt_PT ru sk tg tr_TR vi zh_CN ) +set( _tx_complete az az_AZ ca de fi_FI he hi hr ja ko lt pt_BR sq + sv uk zh_TW ) +set( _tx_good as be ca@valencia cs_CZ da fr fur it_IT ml nl pt_PT + ru sk tg tr_TR vi zh_CN ) set( _tx_ok ar ast bg bn el en_GB es es_MX es_PR et eu fa gl hu id - is mr nb pl ro sl sr sr@latin th zh_HK ) + is mr nb pl ro si sl sr sr@latin th zh_HK ) set( _tx_incomplete eo es_PE fr_CH gu id_ID ie kk kn ko_KR lo lv mk - ne ne_NP ru_RU si te ur uz zh ) + ne ne_NP ru_RU te ur uz zh ) ### Required versions # From 4948f634edfef3c7484033a563e0aae4d7dbd998 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 12:06:11 +0200 Subject: [PATCH 81/87] [keyboard] Code tidy - complain just once (globally) if ckbcomp is not found, rather than at every update to the layout. - tighten up QStringList constructor. --- .../keyboard/keyboardwidget/keyboardpreview.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/modules/keyboard/keyboardwidget/keyboardpreview.cpp b/src/modules/keyboard/keyboardwidget/keyboardpreview.cpp index 572965de5..0bb1add87 100644 --- a/src/modules/keyboard/keyboardwidget/keyboardpreview.cpp +++ b/src/modules/keyboard/keyboardwidget/keyboardpreview.cpp @@ -124,10 +124,7 @@ KeyBoardPreview::loadCodes() return false; } - QStringList param; - param << "-model" - << "pc106" - << "-layout" << layout << "-compact"; + QStringList param { "-model", "pc106", "-layout", layout, "-compact" }; if ( !variant.isEmpty() ) { param << "-variant" << variant; @@ -140,13 +137,18 @@ KeyBoardPreview::loadCodes() process.start( "ckbcomp", param ); if ( !process.waitForStarted() ) { - cWarning() << "ckbcomp not found , keyboard preview disabled"; + static bool need_warning = true; + if ( need_warning ) + { + cWarning() << "ckbcomp not found , keyboard preview disabled"; + need_warning = false; + } return false; } if ( !process.waitForFinished() ) { - cWarning() << "ckbcomp failed, keyboard preview disabled"; + cWarning() << "ckbcomp failed, keyboard preview skipped for" << layout << variant; return false; } From 6017420dded3c35bbc9cf34c311c2fd2b29f31c7 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 12:35:24 +0200 Subject: [PATCH 82/87] [welcome] Fix up and expand tests - improve logging - fix failing tests -- the observed and expected behavior is to fill in a fallback check-URL, not change to an empty list, - **except** if there's no requirements key in the config at all; this is a bit weird, but let's make the tests document existing behavior so we can notice if it changes. --- src/modules/welcome/Tests.cpp | 57 ++++++++++++++++++- .../welcome/checker/GeneralRequirements.cpp | 11 ++-- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/modules/welcome/Tests.cpp b/src/modules/welcome/Tests.cpp index 0445433e9..1e1d07c4b 100644 --- a/src/modules/welcome/Tests.cpp +++ b/src/modules/welcome/Tests.cpp @@ -31,6 +31,8 @@ private Q_SLOTS: void testOneUrl(); void testUrls_data(); void testUrls(); + + void testBadConfigDoesNotResetUrls(); }; WelcomeTests::WelcomeTests() {} @@ -81,9 +83,9 @@ WelcomeTests::testUrls_data() QTest::newRow( "one " ) << QString( "1a-checkinternet.conf" ) << 1; QTest::newRow( "none " ) << QString( "1b-checkinternet.conf" ) << 0; - QTest::newRow( "blank" ) << QString( "1c-checkinternet.conf" ) << 0; - QTest::newRow( "bogus" ) << QString( "1d-checkinternet.conf" ) << 0; - QTest::newRow( "[] " ) << QString( "1e-checkinternet.conf" ) << 0; + QTest::newRow( "blank" ) << QString( "1c-checkinternet.conf" ) << 1; + QTest::newRow( "bogus" ) << QString( "1d-checkinternet.conf" ) << 1; + QTest::newRow( "[] " ) << QString( "1e-checkinternet.conf" ) << 1; QTest::newRow( "-3 " ) << QString( "1f-checkinternet.conf" ) << 3; QTest::newRow( "[3] " ) << QString( "1g-checkinternet.conf" ) << 3; QTest::newRow( "some " ) << QString( "1h-checkinternet.conf" ) << 3; @@ -105,10 +107,59 @@ WelcomeTests::testUrls() const auto map = CalamaresUtils::loadYaml( fi, &ok ); QVERIFY( ok ); + CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( QVector< QUrl > {} ); + QCOMPARE( CalamaresUtils::Network::Manager::instance().getCheckInternetUrls().count(), 0 ); c.setConfigurationMap( map ); QCOMPARE( CalamaresUtils::Network::Manager::instance().getCheckInternetUrls().count(), result ); } +void +WelcomeTests::testBadConfigDoesNotResetUrls() +{ + auto& nam = CalamaresUtils::Network::Manager::instance(); + CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( QVector< QUrl > {} ); + QCOMPARE( nam.getCheckInternetUrls().count(), 0 ); + nam.setCheckHasInternetUrl( QVector< QUrl > { QUrl( "http://example.com" ), QUrl( "https://www.kde.org" ) } ); + QCOMPARE( nam.getCheckInternetUrls().count(), 2 ); + + Config c; + + // This is slightly surprising: if there is **no** requirements + // configuration, the list of check-URLs is left unchanged. + { + const QString filename = QStringLiteral( "1b-checkinternet.conf" ); // "none" + + // BUILD_AS_TEST is the source-directory path + QFile fi( QString( "%1/tests/%2" ).arg( BUILD_AS_TEST, filename ) ); + QVERIFY( fi.exists() ); + + bool ok = false; + const auto map = CalamaresUtils::loadYaml( fi, &ok ); + QVERIFY( ok ); + + c.setConfigurationMap( map ); + } + QCOMPARE( nam.getCheckInternetUrls().count(), 2 ); + + // But if the config contains a requirements entry, even if broken, + // the list is changed (to the default). + { + const QString filename = QStringLiteral( "1d-checkinternet.conf" ); // "bogus" + + // BUILD_AS_TEST is the source-directory path + QFile fi( QString( "%1/tests/%2" ).arg( BUILD_AS_TEST, filename ) ); + QVERIFY( fi.exists() ); + + bool ok = false; + const auto map = CalamaresUtils::loadYaml( fi, &ok ); + QVERIFY( ok ); + + c.setConfigurationMap( map ); + } + QCOMPARE( nam.getCheckInternetUrls().count(), 1 ); +} + + QTEST_GUILESS_MAIN( WelcomeTests ) #include "utils/moc-warnings.h" diff --git a/src/modules/welcome/checker/GeneralRequirements.cpp b/src/modules/welcome/checker/GeneralRequirements.cpp index 2528cbd9f..ca7219ca4 100644 --- a/src/modules/welcome/checker/GeneralRequirements.cpp +++ b/src/modules/welcome/checker/GeneralRequirements.cpp @@ -215,7 +215,7 @@ GeneralRequirements::checkRequirements() return checkEntries; } -/** @brief Loads the check-internel URLs +/** @brief Loads the check-internet URLs * * There may be zero or one or more URLs specified; returns * @c true if the configuration is incomplete or damaged in some way. @@ -246,8 +246,8 @@ getCheckInternetUrls( const QVariantMap& configurationMap ) if ( urls.empty() ) { - cWarning() << "GeneralRequirements entry 'internetCheckUrl' contains no valid URLs," - << "reverting to default (http://example.com)."; + cWarning() << "GeneralRequirements entry 'internetCheckUrl' contains no valid URLs, " + << "reverting to default (" << exampleUrl << ")."; CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( QUrl( exampleUrl ) ); incomplete = true; } @@ -258,8 +258,9 @@ getCheckInternetUrls( const QVariantMap& configurationMap ) } else { - cWarning() << "GeneralRequirements entry 'internetCheckUrl' is undefined in welcome.conf," - "reverting to default (http://example.com)."; + cWarning() << "GeneralRequirements entry 'internetCheckUrl' is undefined in welcome.conf, " + "reverting to default (" + << exampleUrl << ")."; CalamaresUtils::Network::Manager::instance().setCheckHasInternetUrl( QUrl( exampleUrl ) ); incomplete = true; } From 563c1492178c30bbcb3cc1f09bfb2f19fdcc98a5 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 11:56:50 +0200 Subject: [PATCH 83/87] i18n: update english sources, too --- lang/calamares_en.ts | 404 ++++++++++++++++---------------- lang/python.pot | 543 +++++++++++++++++++++---------------------- 2 files changed, 460 insertions(+), 487 deletions(-) diff --git a/lang/calamares_en.ts b/lang/calamares_en.ts index 5ff148ecc..53882fbb0 100644 --- a/lang/calamares_en.ts +++ b/lang/calamares_en.ts @@ -495,12 +495,12 @@ The installer will quit and all changes will be lost. CalamaresWindow - + %1 Setup Program %1 Setup Program - + %1 Installer %1 Installer @@ -539,149 +539,149 @@ The installer will quit and all changes will be lost. Form
    - + Select storage de&vice: Select storage de&vice: - - - - + + + + Current: Current: - + After: After: - + <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. <strong>Manual partitioning</strong><br/>You can create or resize partitions yourself. - + Reuse %1 as home partition for %2. Reuse %1 as home partition for %2. - + <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> <strong>Select a partition to shrink, then drag the bottom bar to resize</strong> - + %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. %1 will be shrunk to %2MiB and a new %3MiB partition will be created for %4. - + Boot loader location: Boot loader location: - + <strong>Select a partition to install on</strong> <strong>Select a partition to install on</strong> - + An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. An EFI system partition cannot be found anywhere on this system. Please go back and use manual partitioning to set up %1. - + The EFI system partition at %1 will be used for starting %2. The EFI system partition at %1 will be used for starting %2. - + EFI system partition: EFI system partition: - + This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device does not seem to have an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - - - - + + + + <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. <strong>Erase disk</strong><br/>This will <font color="red">delete</font> all data currently present on the selected storage device. - - - - + + + + <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. <strong>Install alongside</strong><br/>The installer will shrink a partition to make room for %1. - - - - + + + + <strong>Replace a partition</strong><br/>Replaces a partition with %1. <strong>Replace a partition</strong><br/>Replaces a partition with %1. - + This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has %1 on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device already has an operating system on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. This storage device has multiple operating systems on it. What would you like to do?<br/>You will be able to review and confirm your choices before any change is made to the storage device. - + This storage device already has an operating system on it, but the partition table <strong>%1</strong> is different from the needed <strong>%2</strong>.<br/> This storage device already has an operating system on it, but the partition table <strong>%1</strong> is different from the needed <strong>%2</strong>.<br/> - + This storage device has one of its partitions <strong>mounted</strong>. This storage device has one of its partitions <strong>mounted</strong>. - + This storage device is a part of an <strong>inactive RAID</strong> device. This storage device is a part of an <strong>inactive RAID</strong> device. - + No Swap No Swap - + Reuse Swap Reuse Swap - + Swap (no Hibernate) Swap (no Hibernate) - + Swap (with Hibernate) Swap (with Hibernate) - + Swap to file Swap to file @@ -749,12 +749,12 @@ The installer will quit and all changes will be lost. Config - + Set keyboard model to %1.<br/> Set keyboard model to %1.<br/> - + Set keyboard layout to %1/%2. Set keyboard layout to %1/%2. @@ -804,47 +804,47 @@ The installer will quit and all changes will be lost. Network Installation. (Disabled: Unable to fetch package lists, check your network connection)
    - + This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. <a href="#details">Details...</a> This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. <a href="#details">Details...</a> - + This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> - + This computer does not satisfy some of the recommended requirements for setting up %1.<br/>Setup can continue, but some features might be disabled. This computer does not satisfy some of the recommended requirements for setting up %1.<br/>Setup can continue, but some features might be disabled. - + This computer does not satisfy some of the recommended requirements for installing %1.<br/>Installation can continue, but some features might be disabled. This computer does not satisfy some of the recommended requirements for installing %1.<br/>Installation can continue, but some features might be disabled. - + This program will ask you some questions and set up %2 on your computer. This program will ask you some questions and set up %2 on your computer. - + <h1>Welcome to the Calamares setup program for %1</h1> <h1>Welcome to the Calamares setup program for %1</h1> - + <h1>Welcome to %1 setup</h1> <h1>Welcome to %1 setup</h1> - + <h1>Welcome to the Calamares installer for %1</h1> <h1>Welcome to the Calamares installer for %1</h1> - + <h1>Welcome to the %1 installer</h1> <h1>Welcome to the %1 installer</h1> @@ -939,15 +939,40 @@ The installer will quit and all changes will be lost. The installation of %1 is complete.
    - + Package Selection Package Selection - + Please pick a product from the list. The selected product will be installed. Please pick a product from the list. The selected product will be installed. + + + Install option: <strong>%1</strong> + + + + + None + + + + + Summary + Summary + + + + This is an overview of what will happen once you start the setup procedure. + This is an overview of what will happen once you start the setup procedure. + + + + This is an overview of what will happen once you start the install procedure. + This is an overview of what will happen once you start the install procedure. + ContextualProcessJob @@ -2446,6 +2471,14 @@ The installer will quit and all changes will be lost. Please pick a product from the list. The selected product will be installed.
    + + PackageChooserQmlViewStep + + + Packages + Packages + + PackageChooserViewStep @@ -2729,17 +2762,17 @@ The installer will quit and all changes will be lost. I&nstall boot loader on: - + Are you sure you want to create a new partition table on %1? Are you sure you want to create a new partition table on %1? - + Can not create new partition Can not create new partition - + The partition table on %1 already has %2 primary partitions, and no more can be added. Please remove one primary partition and add an extended partition, instead. The partition table on %1 already has %2 primary partitions, and no more can be added. Please remove one primary partition and add an extended partition, instead. @@ -2757,107 +2790,82 @@ The installer will quit and all changes will be lost. Partitions
    - - Install %1 <strong>alongside</strong> another operating system. - Install %1 <strong>alongside</strong> another operating system. - - - - <strong>Erase</strong> disk and install %1. - <strong>Erase</strong> disk and install %1. - - - - <strong>Replace</strong> a partition with %1. - <strong>Replace</strong> a partition with %1. - - - - <strong>Manual</strong> partitioning. - <strong>Manual</strong> partitioning. - - - - Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). - Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3). - - - - <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. - <strong>Erase</strong> disk <strong>%2</strong> (%3) and install %1. - - - - <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. - <strong>Replace</strong> a partition on disk <strong>%2</strong> (%3) with %1. - - - - <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). - <strong>Manual</strong> partitioning on disk <strong>%1</strong> (%2). - - - - Disk <strong>%1</strong> (%2) - Disk <strong>%1</strong> (%2) - - - + Current: Current: - + After: After: - + No EFI system partition configured No EFI system partition configured - - An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a FAT32 filesystem with the <strong>%3</strong> flag enabled and mount point <strong>%2</strong>.<br/><br/>You can continue without setting up an EFI system partition but your system may fail to start. - An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a FAT32 filesystem with the <strong>%3</strong> flag enabled and mount point <strong>%2</strong>.<br/><br/>You can continue without setting up an EFI system partition but your system may fail to start. + + EFI system partition configured incorrectly + - - An EFI system partition is necessary to start %1.<br/><br/>A partition was configured with mount point <strong>%2</strong> but its <strong>%3</strong> flag is not set.<br/>To set the flag, go back and edit the partition.<br/><br/>You can continue without setting the flag but your system may fail to start. - An EFI system partition is necessary to start %1.<br/><br/>A partition was configured with mount point <strong>%2</strong> but its <strong>%3</strong> flag is not set.<br/>To set the flag, go back and edit the partition.<br/><br/>You can continue without setting the flag but your system may fail to start. + + An EFI system partition is necessary to start %1.<br/><br/>To configure an EFI system partition, go back and select or create a suitable filesystem. + - - EFI system partition flag not set - EFI system partition flag not set + + The filesystem must be mounted on <strong>%1</strong>. + - + + The filesystem must have type FAT32. + + + + + The filesystem must be at least %1 MiB in size. + + + + + The filesystem must have flag <strong>%1</strong> set. + + + + + You can continue without setting up an EFI system partition but your system may fail to start. + + + + Option to use GPT on BIOS Option to use GPT on BIOS - + A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.<br/><br/>To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the <strong>bios_grub</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.<br/><br/>To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the <strong>bios_grub</strong> flag enabled.<br/><br/>An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT. - + Boot partition not encrypted Boot partition not encrypted - + A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.<br/><br/>There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.<br/>You may continue if you wish, but filesystem unlocking will happen later during system startup.<br/>To encrypt the boot partition, go back and recreate it, selecting <strong>Encrypt</strong> in the partition creation window. A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.<br/><br/>There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.<br/>You may continue if you wish, but filesystem unlocking will happen later during system startup.<br/>To encrypt the boot partition, go back and recreate it, selecting <strong>Encrypt</strong> in the partition creation window. - + has at least one disk device available. has at least one disk device available. - + There are no partitions to install on. There are no partitions to install on. @@ -2992,7 +3000,7 @@ Output: QObject - + %1 (%2) %1 (%2) @@ -3318,44 +3326,16 @@ Output: ResultsListDialog - + For best results, please ensure that this computer: For best results, please ensure that this computer: - + System requirements System requirements - - ResultsListWidget - - - This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. <a href="#details">Details...</a> - This computer does not satisfy the minimum requirements for setting up %1.<br/>Setup cannot continue. <a href="#details">Details...</a> - - - - This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> - This computer does not satisfy the minimum requirements for installing %1.<br/>Installation cannot continue. <a href="#details">Details...</a> - - - - This computer does not satisfy some of the recommended requirements for setting up %1.<br/>Setup can continue, but some features might be disabled. - This computer does not satisfy some of the recommended requirements for setting up %1.<br/>Setup can continue, but some features might be disabled. - - - - This computer does not satisfy some of the recommended requirements for installing %1.<br/>Installation can continue, but some features might be disabled. - This computer does not satisfy some of the recommended requirements for installing %1.<br/>Installation can continue, but some features might be disabled. - - - - This program will ask you some questions and set up %2 on your computer. - This program will ask you some questions and set up %2 on your computer. - - ScanningDialog @@ -3647,27 +3627,6 @@ Output: %L1 / %L2 - - SummaryPage - - - This is an overview of what will happen once you start the setup procedure. - This is an overview of what will happen once you start the setup procedure. - - - - This is an overview of what will happen once you start the install procedure. - This is an overview of what will happen once you start the install procedure. - - - - SummaryViewStep - - - Summary - Summary - - TrackingInstallJob @@ -3999,7 +3958,7 @@ Output: WelcomeQmlViewStep - + Welcome Welcome @@ -4007,7 +3966,7 @@ Output: WelcomeViewStep - + Welcome Welcome @@ -4090,21 +4049,21 @@ Output: i18n - + <h1>Languages</h1> </br> The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. <h1>Languages</h1> </br> The system locale setting affects the language and character set for some command line user interface elements. The current setting is <strong>%1</strong>. - + <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. <h1>Locales</h1> </br> The system locale setting affects the numbers and dates format. The current setting is <strong>%1</strong>. - + Back Back @@ -4170,6 +4129,45 @@ Output: <p>These are example release notes.</p> + + packagechooserq + + + LibreOffice is a powerful and free office suite, used by millions of people around the world. It includes several applications that make it the most versatile Free and Open Source office suite on the market.<br/> + Default option. + + + + + LibreOffice + + + + + If you don't want to install an office suite, just select No Office Suite. You can always add one (or more) later on your installed system as the need arrives. + + + + + No Office Suite + + + + + Create a minimal Desktop install, remove all extra applications and decide later on what you would like to add to your system. Examples of what won't be on such an install, there will be no Office Suite, no media players, no image viewer or print support. It will be just a desktop, file browser, package manager, text editor and simple web-browser. + + + + + Minimal Install + + + + + Please select an option for your install, or use the default: LibreOffice included. + + + release_notes @@ -4226,132 +4224,132 @@ Output: usersq - + Pick your user name and credentials to login and perform admin tasks Pick your user name and credentials to login and perform admin tasks - + What is your name? What is your name? - + Your Full Name Your Full Name - + What name do you want to use to log in? What name do you want to use to log in? - + Login Name Login Name - + If more than one person will use this computer, you can create multiple accounts after installation. If more than one person will use this computer, you can create multiple accounts after installation. - + Only lowercase letters, numbers, underscore and hyphen are allowed. Only lowercase letters, numbers, underscore and hyphen are allowed. - + root is not allowed as username. root is not allowed as username. - + What is the name of this computer? What is the name of this computer? - + Computer Name Computer Name - + This name will be used if you make the computer visible to others on a network. This name will be used if you make the computer visible to others on a network. - + localhost is not allowed as hostname. localhost is not allowed as hostname. - + Choose a password to keep your account safe. Choose a password to keep your account safe. - + Password Password - + Repeat Password Repeat Password - + 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. 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. - + Validate passwords quality Validate passwords quality - + When this box is checked, password-strength checking is done and you will not be able to use a weak password. When this box is checked, password-strength checking is done and you will not be able to use a weak password. - + Log in automatically without asking for the password Log in automatically without asking for the password - + Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. Only letters, numbers, underscore and hyphen are allowed, minimal of two characters. - + Reuse user password as root password Reuse user password as root password - + Use the same password for the administrator account. Use the same password for the administrator account. - + Choose a root password to keep your account safe. Choose a root password to keep your account safe. - + Root Password Root Password - + Repeat Root Password Repeat Root Password - + Enter the same password twice, so that it can be checked for typing errors. Enter the same password twice, so that it can be checked for typing errors. diff --git a/lang/python.pot b/lang/python.pot index a3d128ca7..6bd8bfd92 100644 --- a/lang/python.pot +++ b/lang/python.pot @@ -2,406 +2,381 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-14 12:55+0200\n" +"POT-Creation-Date: 2021-09-06 11:40+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" +"Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: \n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: src/modules/grubcfg/main.py:28 -msgid "Configure GRUB." -msgstr "Configure GRUB." - -#: src/modules/mount/main.py:30 -msgid "Mounting partitions." -msgstr "Mounting partitions." - -#: src/modules/mount/main.py:144 src/modules/initcpiocfg/main.py:197 -#: src/modules/initcpiocfg/main.py:201 -#: src/modules/luksopenswaphookcfg/main.py:86 -#: src/modules/luksopenswaphookcfg/main.py:90 src/modules/rawfs/main.py:164 -#: src/modules/initramfscfg/main.py:85 src/modules/initramfscfg/main.py:89 -#: src/modules/openrcdmcryptcfg/main.py:72 -#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/fstab/main.py:355 -#: src/modules/fstab/main.py:361 src/modules/fstab/main.py:388 -#: src/modules/localecfg/main.py:135 src/modules/networkcfg/main.py:39 -msgid "Configuration Error" -msgstr "Configuration Error" - -#: src/modules/mount/main.py:145 src/modules/initcpiocfg/main.py:198 -#: src/modules/luksopenswaphookcfg/main.py:87 src/modules/rawfs/main.py:165 -#: src/modules/initramfscfg/main.py:86 src/modules/openrcdmcryptcfg/main.py:73 -#: src/modules/fstab/main.py:356 -msgid "No partitions are defined for
    {!s}
    to use." -msgstr "No partitions are defined for
    {!s}
    to use." - -#: src/modules/services-systemd/main.py:26 -msgid "Configure systemd services" -msgstr "Configure systemd services" - -#: src/modules/services-systemd/main.py:59 -#: src/modules/services-openrc/main.py:93 -msgid "Cannot modify service" -msgstr "Cannot modify service" - -#: src/modules/services-systemd/main.py:60 -msgid "" -"systemctl {arg!s} call in chroot returned error code {num!s}." +#: src/modules/bootloader/main.py:43 +msgid "Install bootloader." msgstr "" -"systemctl {arg!s} call in chroot returned error code {num!s}." -#: src/modules/services-systemd/main.py:63 -#: src/modules/services-systemd/main.py:67 -msgid "Cannot enable systemd service {name!s}." -msgstr "Cannot enable systemd service {name!s}." - -#: src/modules/services-systemd/main.py:65 -msgid "Cannot enable systemd target {name!s}." -msgstr "Cannot enable systemd target {name!s}." - -#: src/modules/services-systemd/main.py:69 -msgid "Cannot disable systemd target {name!s}." -msgstr "Cannot disable systemd target {name!s}." - -#: src/modules/services-systemd/main.py:71 -msgid "Cannot mask systemd unit {name!s}." -msgstr "Cannot mask systemd unit {name!s}." - -#: src/modules/services-systemd/main.py:73 -msgid "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." +#: src/modules/bootloader/main.py:508 +msgid "Bootloader installation error" msgstr "" -"Unknown systemd commands {command!s} and " -"{suffix!s} for unit {name!s}." -#: src/modules/umount/main.py:31 -msgid "Unmount file systems." -msgstr "Unmount file systems." - -#: src/modules/unpackfs/main.py:35 -msgid "Filling up filesystems." -msgstr "Filling up filesystems." - -#: src/modules/unpackfs/main.py:255 -msgid "rsync failed with error code {}." -msgstr "rsync failed with error code {}." - -#: src/modules/unpackfs/main.py:300 -msgid "Unpacking image {}/{}, file {}/{}" -msgstr "Unpacking image {}/{}, file {}/{}" - -#: src/modules/unpackfs/main.py:315 -msgid "Starting to unpack {}" -msgstr "Starting to unpack {}" - -#: src/modules/unpackfs/main.py:324 src/modules/unpackfs/main.py:464 -msgid "Failed to unpack image \"{}\"" -msgstr "Failed to unpack image \"{}\"" - -#: src/modules/unpackfs/main.py:431 -msgid "No mount point for root partition" -msgstr "No mount point for root partition" - -#: src/modules/unpackfs/main.py:432 -msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" -msgstr "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" - -#: src/modules/unpackfs/main.py:437 -msgid "Bad mount point for root partition" -msgstr "Bad mount point for root partition" - -#: src/modules/unpackfs/main.py:438 -msgid "rootMountPoint is \"{}\", which does not exist, doing nothing" -msgstr "rootMountPoint is \"{}\", which does not exist, doing nothing" - -#: src/modules/unpackfs/main.py:454 src/modules/unpackfs/main.py:458 -#: src/modules/unpackfs/main.py:478 -msgid "Bad unsquash configuration" -msgstr "Bad unsquash configuration" - -#: src/modules/unpackfs/main.py:455 -msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel" -msgstr "The filesystem for \"{}\" ({}) is not supported by your current kernel" - -#: src/modules/unpackfs/main.py:459 -msgid "The source filesystem \"{}\" does not exist" -msgstr "The source filesystem \"{}\" does not exist" - -#: src/modules/unpackfs/main.py:465 +#: src/modules/bootloader/main.py:509 msgid "" -"Failed to find unsquashfs, make sure you have the squashfs-tools package " -"installed" +"The bootloader could not be installed. The installation command
    {!s} returned error code {!s}."
     msgstr ""
    -"Failed to find unsquashfs, make sure you have the squashfs-tools package "
    -"installed"
    -
    -#: src/modules/unpackfs/main.py:479
    -msgid "The destination \"{}\" in the target system is not a directory"
    -msgstr "The destination \"{}\" in the target system is not a directory"
     
     #: src/modules/displaymanager/main.py:526
     msgid "Cannot write KDM configuration file"
    -msgstr "Cannot write KDM configuration file"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:527
     msgid "KDM config file {!s} does not exist"
    -msgstr "KDM config file {!s} does not exist"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:588
     msgid "Cannot write LXDM configuration file"
    -msgstr "Cannot write LXDM configuration file"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:589
     msgid "LXDM config file {!s} does not exist"
    -msgstr "LXDM config file {!s} does not exist"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:672
     msgid "Cannot write LightDM configuration file"
    -msgstr "Cannot write LightDM configuration file"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:673
     msgid "LightDM config file {!s} does not exist"
    -msgstr "LightDM config file {!s} does not exist"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:747
     msgid "Cannot configure LightDM"
    -msgstr "Cannot configure LightDM"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:748
     msgid "No LightDM greeter installed."
    -msgstr "No LightDM greeter installed."
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:779
     msgid "Cannot write SLIM configuration file"
    -msgstr "Cannot write SLIM configuration file"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:780
     msgid "SLIM config file {!s} does not exist"
    -msgstr "SLIM config file {!s} does not exist"
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:906
     msgid "No display managers selected for the displaymanager module."
    -msgstr "No display managers selected for the displaymanager module."
    +msgstr ""
     
     #: src/modules/displaymanager/main.py:907
     msgid ""
     "The displaymanagers list is empty or undefined in both globalstorage and "
     "displaymanager.conf."
     msgstr ""
    -"The displaymanagers list is empty or undefined in both globalstorage and "
    -"displaymanager.conf."
     
     #: src/modules/displaymanager/main.py:989
     msgid "Display manager configuration was incomplete"
    -msgstr "Display manager configuration was incomplete"
    +msgstr ""
    +
    +#: src/modules/dracut/main.py:27
    +msgid "Creating initramfs with dracut."
    +msgstr ""
    +
    +#: src/modules/dracut/main.py:49
    +msgid "Failed to run dracut on the target"
    +msgstr ""
    +
    +#: src/modules/dracut/main.py:50 src/modules/mkinitfs/main.py:50
    +msgid "The exit code was {}"
    +msgstr ""
    +
    +#: src/modules/dummypython/main.py:35
    +msgid "Dummy python job."
    +msgstr ""
    +
    +#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93
    +#: src/modules/dummypython/main.py:94
    +msgid "Dummy python step {}"
    +msgstr ""
    +
    +#: src/modules/fstab/main.py:29
    +msgid "Writing fstab."
    +msgstr ""
    +
    +#: src/modules/fstab/main.py:355 src/modules/fstab/main.py:361
    +#: src/modules/fstab/main.py:388 src/modules/initcpiocfg/main.py:197
    +#: src/modules/initcpiocfg/main.py:201 src/modules/initramfscfg/main.py:85
    +#: src/modules/initramfscfg/main.py:89 src/modules/localecfg/main.py:135
    +#: src/modules/luksopenswaphookcfg/main.py:86
    +#: src/modules/luksopenswaphookcfg/main.py:90 src/modules/mount/main.py:144
    +#: src/modules/networkcfg/main.py:42 src/modules/openrcdmcryptcfg/main.py:72
    +#: src/modules/openrcdmcryptcfg/main.py:76 src/modules/rawfs/main.py:164
    +msgid "Configuration Error"
    +msgstr ""
    +
    +#: src/modules/fstab/main.py:356 src/modules/initcpiocfg/main.py:198
    +#: src/modules/initramfscfg/main.py:86
    +#: src/modules/luksopenswaphookcfg/main.py:87 src/modules/mount/main.py:145
    +#: src/modules/openrcdmcryptcfg/main.py:73 src/modules/rawfs/main.py:165
    +msgid "No partitions are defined for 
    {!s}
    to use." +msgstr "" + +#: src/modules/fstab/main.py:362 src/modules/initcpiocfg/main.py:202 +#: src/modules/initramfscfg/main.py:90 src/modules/localecfg/main.py:136 +#: src/modules/luksopenswaphookcfg/main.py:91 src/modules/networkcfg/main.py:43 +#: src/modules/openrcdmcryptcfg/main.py:77 +msgid "No root mount point is given for
    {!s}
    to use." +msgstr "" + +#: src/modules/fstab/main.py:389 +msgid "No
    {!s}
    configuration is given for
    {!s}
    to use." +msgstr "" + +#: src/modules/grubcfg/main.py:28 +msgid "Configure GRUB." +msgstr "" + +#: src/modules/hwclock/main.py:26 +msgid "Setting hardware clock." +msgstr "" #: src/modules/initcpiocfg/main.py:28 msgid "Configuring mkinitcpio." -msgstr "Configuring mkinitcpio." +msgstr "" -#: src/modules/initcpiocfg/main.py:202 -#: src/modules/luksopenswaphookcfg/main.py:91 -#: src/modules/initramfscfg/main.py:90 src/modules/openrcdmcryptcfg/main.py:77 -#: src/modules/fstab/main.py:362 src/modules/localecfg/main.py:136 -#: src/modules/networkcfg/main.py:40 -msgid "No root mount point is given for
    {!s}
    to use." -msgstr "No root mount point is given for
    {!s}
    to use." +#: src/modules/initramfscfg/main.py:32 +msgid "Configuring initramfs." +msgstr "" + +#: src/modules/localecfg/main.py:30 +msgid "Configuring locales." +msgstr "" #: src/modules/luksopenswaphookcfg/main.py:26 msgid "Configuring encrypted swap." -msgstr "Configuring encrypted swap." +msgstr "" + +#: src/modules/mkinitfs/main.py:27 +msgid "Creating initramfs with mkinitfs." +msgstr "" + +#: src/modules/mkinitfs/main.py:49 +msgid "Failed to run mkinitfs on the target" +msgstr "" + +#: src/modules/mount/main.py:30 +msgid "Mounting partitions." +msgstr "" + +#: src/modules/networkcfg/main.py:29 +msgid "Saving network configuration." +msgstr "" + +#: src/modules/openrcdmcryptcfg/main.py:26 +msgid "Configuring OpenRC dmcrypt service." +msgstr "" + +#: src/modules/packages/main.py:50 src/modules/packages/main.py:59 +#: src/modules/packages/main.py:69 +msgid "Install packages." +msgstr "" + +#: src/modules/packages/main.py:57 +#, python-format +msgid "Processing packages (%(count)d / %(total)d)" +msgstr "" + +#: src/modules/packages/main.py:62 +#, python-format +msgid "Installing one package." +msgid_plural "Installing %(num)d packages." +msgstr[0] "" +msgstr[1] "" + +#: src/modules/packages/main.py:65 +#, python-format +msgid "Removing one package." +msgid_plural "Removing %(num)d packages." +msgstr[0] "" +msgstr[1] "" + +#: src/modules/packages/main.py:638 src/modules/packages/main.py:650 +#: src/modules/packages/main.py:678 +msgid "Package Manager error" +msgstr "" + +#: src/modules/packages/main.py:639 +msgid "" +"The package manager could not prepare updates. The command
    {!s}
    " +"returned error code {!s}." +msgstr "" + +#: src/modules/packages/main.py:651 +msgid "" +"The package manager could not update the system. The command
    {!s}
    " +"returned error code {!s}." +msgstr "" + +#: src/modules/packages/main.py:679 +msgid "" +"The package manager could not make changes to the installed system. The " +"command
    {!s}
    returned error code {!s}." +msgstr "" + +#: src/modules/plymouthcfg/main.py:27 +msgid "Configure Plymouth theme" +msgstr "" #: src/modules/rawfs/main.py:26 msgid "Installing data." -msgstr "Installing data." +msgstr "" #: src/modules/services-openrc/main.py:29 msgid "Configure OpenRC services" -msgstr "Configure OpenRC services" +msgstr "" #: src/modules/services-openrc/main.py:57 msgid "Cannot add service {name!s} to run-level {level!s}." -msgstr "Cannot add service {name!s} to run-level {level!s}." +msgstr "" #: src/modules/services-openrc/main.py:59 msgid "Cannot remove service {name!s} from run-level {level!s}." -msgstr "Cannot remove service {name!s} from run-level {level!s}." +msgstr "" #: src/modules/services-openrc/main.py:61 msgid "" "Unknown service-action {arg!s} for service {name!s} in run-" "level {level!s}." msgstr "" -"Unknown service-action {arg!s} for service {name!s} in run-" -"level {level!s}." + +#: src/modules/services-openrc/main.py:93 +#: src/modules/services-systemd/main.py:59 +msgid "Cannot modify service" +msgstr "" #: src/modules/services-openrc/main.py:94 msgid "" "rc-update {arg!s} call in chroot returned error code {num!s}." msgstr "" -"rc-update {arg!s} call in chroot returned error code {num!s}." #: src/modules/services-openrc/main.py:101 msgid "Target runlevel does not exist" -msgstr "Target runlevel does not exist" +msgstr "" #: src/modules/services-openrc/main.py:102 msgid "" "The path for runlevel {level!s} is {path!s}, which does not " "exist." msgstr "" -"The path for runlevel {level!s} is {path!s}, which does not " -"exist." #: src/modules/services-openrc/main.py:110 msgid "Target service does not exist" -msgstr "Target service does not exist" +msgstr "" #: src/modules/services-openrc/main.py:111 msgid "" -"The path for service {name!s} is {path!s}, which does not " -"exist." +"The path for service {name!s} is {path!s}, which does not exist." msgstr "" -"The path for service {name!s} is {path!s}, which does not " -"exist." -#: src/modules/plymouthcfg/main.py:27 -msgid "Configure Plymouth theme" -msgstr "Configure Plymouth theme" +#: src/modules/services-systemd/main.py:26 +msgid "Configure systemd services" +msgstr "" -#: src/modules/packages/main.py:50 src/modules/packages/main.py:59 -#: src/modules/packages/main.py:69 -msgid "Install packages." -msgstr "Install packages." - -#: src/modules/packages/main.py:57 -#, python-format -msgid "Processing packages (%(count)d / %(total)d)" -msgstr "Processing packages (%(count)d / %(total)d)" - -#: src/modules/packages/main.py:62 -#, python-format -msgid "Installing one package." -msgid_plural "Installing %(num)d packages." -msgstr[0] "Installing one package." -msgstr[1] "Installing %(num)d packages." - -#: src/modules/packages/main.py:65 -#, python-format -msgid "Removing one package." -msgid_plural "Removing %(num)d packages." -msgstr[0] "Removing one package." -msgstr[1] "Removing %(num)d packages." - -#: src/modules/packages/main.py:588 src/modules/packages/main.py:600 -#: src/modules/packages/main.py:628 -msgid "Package Manager error" -msgstr "Package Manager error" - -#: src/modules/packages/main.py:589 +#: src/modules/services-systemd/main.py:60 msgid "" -"The package manager could not prepare updates. The command
    {!s}
    " -"returned error code {!s}." +"systemctl {arg!s} call in chroot returned error code {num!s}." msgstr "" -"The package manager could not prepare updates. The command
    {!s}
    " -"returned error code {!s}." -#: src/modules/packages/main.py:601 +#: src/modules/services-systemd/main.py:63 +#: src/modules/services-systemd/main.py:67 +msgid "Cannot enable systemd service {name!s}." +msgstr "" + +#: src/modules/services-systemd/main.py:65 +msgid "Cannot enable systemd target {name!s}." +msgstr "" + +#: src/modules/services-systemd/main.py:69 +msgid "Cannot disable systemd target {name!s}." +msgstr "" + +#: src/modules/services-systemd/main.py:71 +msgid "Cannot mask systemd unit {name!s}." +msgstr "" + +#: src/modules/services-systemd/main.py:73 msgid "" -"The package manager could not update the system. The command
    {!s}
    " -" returned error code {!s}." +"Unknown systemd commands {command!s} and {suffix!s} for unit {name!s}." msgstr "" -"The package manager could not update the system. The command
    {!s}
    " -" returned error code {!s}." -#: src/modules/packages/main.py:629 +#: src/modules/umount/main.py:31 +msgid "Unmount file systems." +msgstr "" + +#: src/modules/unpackfs/main.py:35 +msgid "Filling up filesystems." +msgstr "" + +#: src/modules/unpackfs/main.py:255 +msgid "rsync failed with error code {}." +msgstr "" + +#: src/modules/unpackfs/main.py:300 +msgid "Unpacking image {}/{}, file {}/{}" +msgstr "" + +#: src/modules/unpackfs/main.py:315 +msgid "Starting to unpack {}" +msgstr "" + +#: src/modules/unpackfs/main.py:324 src/modules/unpackfs/main.py:464 +msgid "Failed to unpack image \"{}\"" +msgstr "" + +#: src/modules/unpackfs/main.py:431 +msgid "No mount point for root partition" +msgstr "" + +#: src/modules/unpackfs/main.py:432 +msgid "globalstorage does not contain a \"rootMountPoint\" key, doing nothing" +msgstr "" + +#: src/modules/unpackfs/main.py:437 +msgid "Bad mount point for root partition" +msgstr "" + +#: src/modules/unpackfs/main.py:438 +msgid "rootMountPoint is \"{}\", which does not exist, doing nothing" +msgstr "" + +#: src/modules/unpackfs/main.py:454 src/modules/unpackfs/main.py:458 +#: src/modules/unpackfs/main.py:478 +msgid "Bad unsquash configuration" +msgstr "" + +#: src/modules/unpackfs/main.py:455 +msgid "The filesystem for \"{}\" ({}) is not supported by your current kernel" +msgstr "" + +#: src/modules/unpackfs/main.py:459 +msgid "The source filesystem \"{}\" does not exist" +msgstr "" + +#: src/modules/unpackfs/main.py:465 msgid "" -"The package manager could not make changes to the installed system. The " -"command
    {!s}
    returned error code {!s}." +"Failed to find unsquashfs, make sure you have the squashfs-tools package " +"installed" msgstr "" -"The package manager could not make changes to the installed system. The " -"command
    {!s}
    returned error code {!s}." -#: src/modules/bootloader/main.py:43 -msgid "Install bootloader." -msgstr "Install bootloader." - -#: src/modules/bootloader/main.py:502 -msgid "Bootloader installation error" -msgstr "Bootloader installation error" - -#: src/modules/bootloader/main.py:503 -msgid "" -"The bootloader could not be installed. The installation command " -"
    {!s}
    returned error code {!s}." +#: src/modules/unpackfs/main.py:479 +msgid "The destination \"{}\" in the target system is not a directory" msgstr "" -"The bootloader could not be installed. The installation command " -"
    {!s}
    returned error code {!s}." - -#: src/modules/hwclock/main.py:26 -msgid "Setting hardware clock." -msgstr "Setting hardware clock." - -#: src/modules/mkinitfs/main.py:27 -msgid "Creating initramfs with mkinitfs." -msgstr "Creating initramfs with mkinitfs." - -#: src/modules/mkinitfs/main.py:49 -msgid "Failed to run mkinitfs on the target" -msgstr "Failed to run mkinitfs on the target" - -#: src/modules/mkinitfs/main.py:50 src/modules/dracut/main.py:50 -msgid "The exit code was {}" -msgstr "The exit code was {}" - -#: src/modules/dracut/main.py:27 -msgid "Creating initramfs with dracut." -msgstr "Creating initramfs with dracut." - -#: src/modules/dracut/main.py:49 -msgid "Failed to run dracut on the target" -msgstr "Failed to run dracut on the target" - -#: src/modules/initramfscfg/main.py:32 -msgid "Configuring initramfs." -msgstr "Configuring initramfs." - -#: src/modules/openrcdmcryptcfg/main.py:26 -msgid "Configuring OpenRC dmcrypt service." -msgstr "Configuring OpenRC dmcrypt service." - -#: src/modules/fstab/main.py:29 -msgid "Writing fstab." -msgstr "Writing fstab." - -#: src/modules/fstab/main.py:389 -msgid "No
    {!s}
    configuration is given for
    {!s}
    to use." -msgstr "No
    {!s}
    configuration is given for
    {!s}
    to use." - -#: src/modules/dummypython/main.py:35 -msgid "Dummy python job." -msgstr "Dummy python job." - -#: src/modules/dummypython/main.py:37 src/modules/dummypython/main.py:93 -#: src/modules/dummypython/main.py:94 -msgid "Dummy python step {}" -msgstr "Dummy python step {}" - -#: src/modules/localecfg/main.py:30 -msgid "Configuring locales." -msgstr "Configuring locales." - -#: src/modules/networkcfg/main.py:28 -msgid "Saving network configuration." -msgstr "Saving network configuration." From b237c73a04e01f845e318242ee72dad662fdb809 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 12:49:49 +0200 Subject: [PATCH 84/87] [shellprocess] Fix test The test was loading the config file (for testing) either from the build directory or possibly the source directory; if the config in the build-dir was edited (for other testing purposes) then the test would fail. Load only the source-dir version of the file. --- src/modules/shellprocess/Tests.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/modules/shellprocess/Tests.cpp b/src/modules/shellprocess/Tests.cpp index 77368db48..c1489c1ab 100644 --- a/src/modules/shellprocess/Tests.cpp +++ b/src/modules/shellprocess/Tests.cpp @@ -42,16 +42,11 @@ ShellProcessTests::testProcessListSampleConfig() { YAML::Node doc; - QStringList dirs { "src/modules/shellprocess", "." }; - for ( const auto& dir : dirs ) - { - QString filename = dir + "/shellprocess.conf"; - if ( QFileInfo::exists( filename ) ) - { - doc = YAML::LoadFile( filename.toStdString() ); - break; - } - } + QString filename = QStringLiteral( "shellprocess.conf" ); + QFile fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) ); + + QVERIFY( fi.exists() ); + doc = YAML::LoadFile( fi.fileName().toStdString() ); CommandList cl( CalamaresUtils::yamlMapToVariant( doc ).value( "script" ) ); QVERIFY( !cl.isEmpty() ); From 44e66c1318221a8eb398dba1a6d7f9ff6903a918 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 13:16:23 +0200 Subject: [PATCH 85/87] [keyboard] Fix mapping for India + English - India (when in English) should use the English variant, not Hindi - While here, fix up minor items in code: - Typo in comment - Asturian doesn't need a special case (which didn't match, anyway) - Don't debug-log a country-name that might be entirely wrong (the layout is English, variant "in" but "in" interpreted as a country is Indonesia, and the actually-desired name is eng_in which isn't a QLocale name at all -- just like the Hausa and Igbo special cases) --- src/modules/keyboard/Config.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/modules/keyboard/Config.cpp b/src/modules/keyboard/Config.cpp index b77282a18..7140bd790 100644 --- a/src/modules/keyboard/Config.cpp +++ b/src/modules/keyboard/Config.cpp @@ -467,12 +467,12 @@ Config::guessLocaleKeyboardLayout() { "ar_TN", arabic }, { "ar_YE", arabic }, { "ca_ES", "cat_ES" }, /* Catalan */ - { "as_ES", "ast_ES" }, /* Asturian */ { "en_CA", "us" }, /* Canadian English */ { "el_CY", "gr" }, /* Greek in Cyprus */ - { "el_GR", "gr" }, /* Greek in Greeze */ + { "el_GR", "gr" }, /* Greek in Greece */ { "ig_NG", "igbo_NG" }, /* Igbo in Nigeria */ - { "ha_NG", "hausa_NG" } /* Hausa */ + { "ha_NG", "hausa_NG" }, /* Hausa */ + { "en_IN", "eng_in" }, /* India, English with Rupee */ } ); // Try to preselect a layout, depending on language and locale @@ -508,14 +508,7 @@ Config::guessLocaleKeyboardLayout() } if ( !lang.isEmpty() ) { - const auto langParts = lang.split( '_', SplitSkipEmptyParts ); - - // Note that this his string is not fit for display purposes! - // It doesn't come from QLocale::nativeCountryName. - QString country = QLocale::countryToString( QLocale( lang ).country() ); - cDebug() << Logger::SubEntry << "extracted country" << country << "::" << langParts; - - guessLayout( langParts, m_keyboardLayoutsModel, m_keyboardVariantsModel ); + guessLayout( lang.split( '_', SplitSkipEmptyParts ), m_keyboardLayoutsModel, m_keyboardVariantsModel ); } } From 0aa2603a23b96c3b819ac7d803bdcc43c0349587 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 14:44:48 +0200 Subject: [PATCH 86/87] [libcalamares] Fix network-connectivity check on old Qt With old Qt, Calamares could only run one check on a thread, because the NAM would be switched to NotAccessible -- subsequent checks would fail because the NAM is already hard-set to NotAccessible, so it could never be turned back on by Calamares code. Reset the accessible flag for the NAM while checking if the internet is there. --- src/libcalamares/network/Manager.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libcalamares/network/Manager.cpp b/src/libcalamares/network/Manager.cpp index cceff477e..6cb270ecd 100644 --- a/src/libcalamares/network/Manager.cpp +++ b/src/libcalamares/network/Manager.cpp @@ -162,6 +162,15 @@ Manager::checkHasInternet() { return false; } + // It's possible that access was switched off (see below, if the check + // fails) so we want to turn it back on first. Otherwise all the + // checks will fail **anyway**, defeating the point of the checks. +#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) ) + if ( !d->m_hasInternet ) + { + d->nam()->setNetworkAccessible( QNetworkAccessManager::Accessible ); + } +#endif if ( d->m_lastCheckedUrlIndex < 0 ) { d->m_lastCheckedUrlIndex = 0; @@ -189,8 +198,7 @@ Manager::checkHasInternet() // For earlier Qt versions (< 5.15.0), set the accessibility flag to // NotAccessible if synchronous ping has failed, so that any module // using Qt's networkAccessible method to determine whether or not -// internet connection is actually avaialable won't get confused over -// virtualization technologies. +// internet connection is actually available won't get confused. #if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) ) if ( !d->m_hasInternet ) { @@ -261,6 +269,7 @@ asynchronousRun( QNetworkAccessManager* nam, const QUrl& url, const RequestOptio // Bail out early if the request is bad if ( reply->error() ) { + cWarning() << "Early reply error" << reply->error() << reply->errorString(); reply->deleteLater(); return nullptr; } From ada9a998043e8301e55415e9269b6a32f27efc13 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 6 Sep 2021 15:16:40 +0200 Subject: [PATCH 87/87] Changes: post-release housekeeping --- CHANGES | 12 ++++++++++++ CMakeLists.txt | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 83aa256d3..e0a44f40f 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,18 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. +# 3.2.43 (unreleased) # + +This release contains contributions from (alphabetically by first name): + - No external contributors yet + +## Core ## + - No core changes yet + +## Modules ## + - No module changes yet + + # 3.2.42 (2021-09-06) # This release contains contributions from (alphabetically by first name): diff --git a/CMakeLists.txt b/CMakeLists.txt index f60301ee5..27fcda865 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,11 +41,11 @@ # TODO:3.3: Require CMake 3.12 cmake_minimum_required( VERSION 3.3 FATAL_ERROR ) project( CALAMARES - VERSION 3.2.42 + VERSION 3.2.43 LANGUAGES C CXX ) -set( CALAMARES_VERSION_RC 0 ) # Set to 0 during release cycle, 1 during development +set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development ### OPTIONS #