From ba9a6981b3fcf7fbd099adbcee9f0ce7de96f6ba Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 26 Feb 2019 05:05:10 -0500 Subject: [PATCH 1/6] [welcome] Fix SEGV - In the retranslate function, need to check if the waiting widget still exists. - Tighten up lifetime of the CheckerWidget. --- .../welcome/checker/CheckerContainer.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/modules/welcome/checker/CheckerContainer.cpp b/src/modules/welcome/checker/CheckerContainer.cpp index 6f46cd094..569d2e9cc 100644 --- a/src/modules/welcome/checker/CheckerContainer.cpp +++ b/src/modules/welcome/checker/CheckerContainer.cpp @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2017, Teo Mrnjavac - * Copyright 2017, Adriaan de Groot + * Copyright 2017, 2019, Adriaan de Groot * Copyright 2017, Gabriel Craciunescu * * Calamares is free software: you can redistribute it and/or modify @@ -29,10 +29,10 @@ #include "utils/Retranslator.h" #include "widgets/WaitingWidget.h" -CheckerContainer::CheckerContainer(QWidget* parent) +CheckerContainer::CheckerContainer( QWidget* parent ) : QWidget( parent ) - , m_waitingWidget( new WaitingWidget( QString() ) ) - , m_checkerWidget( new CheckerWidget() ) + , m_waitingWidget( new WaitingWidget( QString(), this ) ) + , m_checkerWidget( nullptr ) , m_verdict( false ) { QBoxLayout* mainLayout = new QHBoxLayout; @@ -40,7 +40,10 @@ CheckerContainer::CheckerContainer(QWidget* parent) CalamaresUtils::unmarginLayout( mainLayout ); mainLayout->addWidget( m_waitingWidget ); - CALAMARES_RETRANSLATE( m_waitingWidget->setText( tr( "Gathering system information..." ) ); ) + CALAMARES_RETRANSLATE( + if ( m_waitingWidget ) + m_waitingWidget->setText( tr( "Gathering system information..." ) ); + ) } CheckerContainer::~CheckerContainer() @@ -51,11 +54,13 @@ CheckerContainer::~CheckerContainer() void CheckerContainer::requirementsComplete( bool ok ) { - m_checkerWidget->init( m_requirements ); + layout()->removeWidget( m_waitingWidget ); m_waitingWidget->deleteLater(); m_waitingWidget = nullptr; // Don't delete in destructor - m_checkerWidget->setParent( this ); + + m_checkerWidget = new CheckerWidget( this ); + m_checkerWidget->init( m_requirements ); layout()->addWidget( m_checkerWidget ); m_verdict = ok; From 1240f63a39c1bde0d9142a6a3d982ca2f1003113 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 26 Feb 2019 05:09:54 -0500 Subject: [PATCH 2/6] [welcome] Rename files to reflect their purpose --- .../welcome/checker/{CheckItemWidget.cpp => ResultWidget.cpp} | 0 src/modules/welcome/checker/{CheckItemWidget.h => ResultWidget.h} | 0 .../welcome/checker/{CheckerWidget.cpp => ResultsListWidget.cpp} | 0 .../welcome/checker/{CheckerWidget.h => ResultsListWidget.h} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/modules/welcome/checker/{CheckItemWidget.cpp => ResultWidget.cpp} (100%) rename src/modules/welcome/checker/{CheckItemWidget.h => ResultWidget.h} (100%) rename src/modules/welcome/checker/{CheckerWidget.cpp => ResultsListWidget.cpp} (100%) rename src/modules/welcome/checker/{CheckerWidget.h => ResultsListWidget.h} (100%) diff --git a/src/modules/welcome/checker/CheckItemWidget.cpp b/src/modules/welcome/checker/ResultWidget.cpp similarity index 100% rename from src/modules/welcome/checker/CheckItemWidget.cpp rename to src/modules/welcome/checker/ResultWidget.cpp diff --git a/src/modules/welcome/checker/CheckItemWidget.h b/src/modules/welcome/checker/ResultWidget.h similarity index 100% rename from src/modules/welcome/checker/CheckItemWidget.h rename to src/modules/welcome/checker/ResultWidget.h diff --git a/src/modules/welcome/checker/CheckerWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp similarity index 100% rename from src/modules/welcome/checker/CheckerWidget.cpp rename to src/modules/welcome/checker/ResultsListWidget.cpp diff --git a/src/modules/welcome/checker/CheckerWidget.h b/src/modules/welcome/checker/ResultsListWidget.h similarity index 100% rename from src/modules/welcome/checker/CheckerWidget.h rename to src/modules/welcome/checker/ResultsListWidget.h From bfb5a4efb9f98f3860b32a6a64a1fc973dc11be0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 26 Feb 2019 06:05:34 -0500 Subject: [PATCH 3/6] [welcome] Chase file renaming - Rename classes inside - Rename include guards --- src/modules/welcome/CMakeLists.txt | 4 +-- .../welcome/checker/CheckerContainer.cpp | 4 +-- .../welcome/checker/CheckerContainer.h | 4 +-- src/modules/welcome/checker/ResultWidget.cpp | 9 +++---- src/modules/welcome/checker/ResultWidget.h | 27 ++++++++++++++----- .../welcome/checker/ResultsListWidget.cpp | 14 +++++----- .../welcome/checker/ResultsListWidget.h | 10 +++---- 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/modules/welcome/CMakeLists.txt b/src/modules/welcome/CMakeLists.txt index 915f5fd2c..f627db032 100644 --- a/src/modules/welcome/CMakeLists.txt +++ b/src/modules/welcome/CMakeLists.txt @@ -16,8 +16,8 @@ include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ) set( CHECKER_SOURCES checker/CheckerContainer.cpp - checker/CheckerWidget.cpp - checker/CheckItemWidget.cpp + checker/ResultWidget.cpp + checker/ResultsListWidget.cpp checker/GeneralRequirements.cpp ${PARTMAN_SRC} ) diff --git a/src/modules/welcome/checker/CheckerContainer.cpp b/src/modules/welcome/checker/CheckerContainer.cpp index 569d2e9cc..0524bddb0 100644 --- a/src/modules/welcome/checker/CheckerContainer.cpp +++ b/src/modules/welcome/checker/CheckerContainer.cpp @@ -22,7 +22,7 @@ #include "CheckerContainer.h" -#include "CheckerWidget.h" +#include "ResultsListWidget.h" #include "utils/CalamaresUtilsGui.h" #include "utils/Logger.h" @@ -59,7 +59,7 @@ void CheckerContainer::requirementsComplete( bool ok ) m_waitingWidget->deleteLater(); m_waitingWidget = nullptr; // Don't delete in destructor - m_checkerWidget = new CheckerWidget( this ); + m_checkerWidget = new ResultsListWidget( this ); m_checkerWidget->init( m_requirements ); layout()->addWidget( m_checkerWidget ); diff --git a/src/modules/welcome/checker/CheckerContainer.h b/src/modules/welcome/checker/CheckerContainer.h index 5a8a1865e..e50b362a2 100644 --- a/src/modules/welcome/checker/CheckerContainer.h +++ b/src/modules/welcome/checker/CheckerContainer.h @@ -27,7 +27,7 @@ #include "modulesystem/Requirement.h" -class CheckerWidget; +class ResultsListWidget; class WaitingWidget; /** @@ -55,7 +55,7 @@ public slots: protected: WaitingWidget *m_waitingWidget; - CheckerWidget *m_checkerWidget; + ResultsListWidget *m_checkerWidget; Calamares::RequirementsList m_requirements; bool m_verdict; diff --git a/src/modules/welcome/checker/ResultWidget.cpp b/src/modules/welcome/checker/ResultWidget.cpp index ef0905100..0a0b438e1 100644 --- a/src/modules/welcome/checker/ResultWidget.cpp +++ b/src/modules/welcome/checker/ResultWidget.cpp @@ -17,7 +17,7 @@ * along with Calamares. If not, see . */ -#include "CheckItemWidget.h" +#include "ResultWidget.h" #include "utils/CalamaresUtilsGui.h" #include "utils/Logger.h" @@ -31,7 +31,7 @@ static inline void setCondition( QLabel* label, CalamaresUtils::ImageType t ) QSize( label->height(), label->height() ) ) ); } -CheckItemWidget::CheckItemWidget( bool checked, +ResultWidget::ResultWidget( bool satisfied, bool required, QWidget* parent ) : QWidget( parent ) @@ -46,8 +46,7 @@ CheckItemWidget::CheckItemWidget( bool checked, mainLayout->addWidget( m_textLabel ); m_textLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); - if ( checked ) - // Condition is satisfied + if ( satisfied ) setCondition( m_iconLabel, CalamaresUtils::StatusOk ); else if ( required ) @@ -58,7 +57,7 @@ CheckItemWidget::CheckItemWidget( bool checked, void -CheckItemWidget::setText( const QString& text ) +ResultWidget::setText( const QString& text ) { m_textLabel->setText( text ); } diff --git a/src/modules/welcome/checker/ResultWidget.h b/src/modules/welcome/checker/ResultWidget.h index d2224c694..d842339ef 100644 --- a/src/modules/welcome/checker/ResultWidget.h +++ b/src/modules/welcome/checker/ResultWidget.h @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac - * Copyright 2017, Adriaan de Groot + * Copyright 2017, 2019, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,22 +17,35 @@ * along with Calamares. If not, see . */ -#ifndef CHECKITEMWIDGET_H -#define CHECKITEMWIDGET_H +#ifndef CHECKER_RESULTWIDGET_H +#define CHECKER_RESULTWIDGET_H #include -class CheckItemWidget : public QWidget +/** + * @brief Displays the results of a single check. + * + * Widget to insert into a ResultListWidget to display an iconic status + * (warning or failure when the check is not satisfied) along with + * descriptive test. + */ +class ResultWidget : public QWidget { Q_OBJECT public: - explicit CheckItemWidget( bool checked, bool required, - QWidget* parent = nullptr ); + /** + * @brief Create widget with results of a check. + * + * Use setText() to set up the text of the widget. + */ + explicit ResultWidget( bool satisfied, bool required, + QWidget* parent = nullptr ); + /// @brief Set the displayed description of the check. void setText( const QString& text ); private: QLabel* m_textLabel; QLabel* m_iconLabel; }; -#endif // CHECKITEMWIDGET_H +#endif // CHECKER_RESULTWIDGET_H diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index 2ad79abfd..1c566026f 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -17,9 +17,9 @@ * along with Calamares. If not, see . */ -#include "CheckerWidget.h" +#include "ResultsListWidget.h" -#include "CheckItemWidget.h" +#include "ResultWidget.h" #include "Branding.h" #include "utils/CalamaresUtilsGui.h" @@ -33,7 +33,7 @@ #include -CheckerWidget::CheckerWidget( QWidget* parent ) +ResultsListWidget::ResultsListWidget( QWidget* parent ) : QWidget( parent ) { setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); @@ -53,7 +53,7 @@ CheckerWidget::CheckerWidget( QWidget* parent ) void -CheckerWidget::init( const Calamares::RequirementsList& checkEntries ) +ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) { bool allChecked = true; bool requirementsSatisfied = true; @@ -62,7 +62,7 @@ CheckerWidget::init( const Calamares::RequirementsList& checkEntries ) { if ( !entry.satisfied ) { - CheckItemWidget* ciw = new CheckItemWidget( entry.satisfied, entry.mandatory ); + ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory ); CALAMARES_RETRANSLATE( ciw->setText( entry.negatedText() ); ) m_entriesLayout->addWidget( ciw ); ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); @@ -162,7 +162,7 @@ CheckerWidget::init( const Calamares::RequirementsList& checkEntries ) void -CheckerWidget::showDetailsDialog( const Calamares::RequirementsList& checkEntries ) +ResultsListWidget::showDetailsDialog( const Calamares::RequirementsList& checkEntries ) { QDialog* detailsDialog = new QDialog( this ); QBoxLayout* mainLayout = new QVBoxLayout; @@ -182,7 +182,7 @@ CheckerWidget::showDetailsDialog( const Calamares::RequirementsList& checkEntrie if ( entry.enumerationText().isEmpty() ) continue; - CheckItemWidget* ciw = new CheckItemWidget( entry.satisfied, entry.mandatory ); + ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory ); CALAMARES_RETRANSLATE( ciw->setText( entry.enumerationText() ); ) entriesLayout->addWidget( ciw ); ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index 81eb6fef0..58bac0602 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -16,19 +16,19 @@ * along with Calamares. If not, see . */ -#ifndef CHECKERWIDGET_H -#define CHECKERWIDGET_H +#ifndef CHECKER_RESULTSLISTWIDGET_H +#define CHECKER_RESULTSLISTWIDGET_H #include "modulesystem/Requirement.h" #include #include -class CheckerWidget : public QWidget +class ResultsListWidget : public QWidget { Q_OBJECT public: - explicit CheckerWidget( QWidget* parent = nullptr ); + explicit ResultsListWidget( QWidget* parent = nullptr ); void init( const Calamares::RequirementsList& checkEntries ); @@ -40,4 +40,4 @@ private: int m_paddingSize; }; -#endif // CHECKERWIDGET_H +#endif // CHECKER_RESULTSLISTWIDGET_H From 54ba0aaf13b0bc2dc948ad88d86040daf08f4b73 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 26 Feb 2019 06:20:20 -0500 Subject: [PATCH 4/6] [welcome] Use convenience function - Introduce a hasDetails() for RequirementEntry, which is just a short-cut, but makes code more readable. --- src/libcalamaresui/modulesystem/Requirement.h | 7 +++++-- src/modules/welcome/checker/ResultsListWidget.cpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libcalamaresui/modulesystem/Requirement.h b/src/libcalamaresui/modulesystem/Requirement.h index bf2157f69..396fe852b 100644 --- a/src/libcalamaresui/modulesystem/Requirement.h +++ b/src/libcalamaresui/modulesystem/Requirement.h @@ -45,14 +45,17 @@ struct RequirementEntry /// @brief name of this requirement; not shown to user and used as ID QString name; - /// @brief Description of this requirement, for use in user-visible lists + /// @brief Detailed description of this requirement, for use in user-visible lists TextFunction enumerationText; - /// @brief User-visible string to show that the requirement is not met + /// @brief User-visible string to show that the requirement is not met, short form TextFunction negatedText; bool satisfied; bool mandatory; + + /// @brief Convenience to check if this entry should be shown in details dialog + bool hasDetails() const { return !enumerationText().isEmpty(); } }; using RequirementsList = QList< RequirementEntry >; diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index 1c566026f..ca1a9459a 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -179,7 +179,7 @@ ResultsListWidget::showDetailsDialog( const Calamares::RequirementsList& checkEn for ( const auto& entry : checkEntries ) { - if ( entry.enumerationText().isEmpty() ) + if ( !entry.hasDetails() ) continue; ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory ); From 4d8acdf425eb3543f437ba2921ea852120df4d7a Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 26 Feb 2019 06:26:20 -0500 Subject: [PATCH 5/6] [welcome] Code-formatting - Update copyright headers - Apply calamaresstyle --- src/modules/welcome/checker/ResultWidget.cpp | 20 +++++++++---------- .../welcome/checker/ResultsListWidget.cpp | 20 ++++++++----------- .../welcome/checker/ResultsListWidget.h | 1 + 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/modules/welcome/checker/ResultWidget.cpp b/src/modules/welcome/checker/ResultWidget.cpp index 0a0b438e1..3c3af62d4 100644 --- a/src/modules/welcome/checker/ResultWidget.cpp +++ b/src/modules/welcome/checker/ResultWidget.cpp @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac - * Copyright 2017, Adriaan de Groot + * Copyright 2017, 2019, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,14 +26,15 @@ static inline void setCondition( QLabel* label, CalamaresUtils::ImageType t ) { - label->setPixmap( CalamaresUtils::defaultPixmap( t, - CalamaresUtils::Original, - QSize( label->height(), label->height() ) ) ); + label->setPixmap( + CalamaresUtils::defaultPixmap( t, + CalamaresUtils::Original, + QSize( label->height(), label->height() ) ) ); } ResultWidget::ResultWidget( bool satisfied, - bool required, - QWidget* parent ) + bool required, + QWidget* parent ) : QWidget( parent ) { QBoxLayout* mainLayout = new QHBoxLayout; @@ -48,11 +49,10 @@ ResultWidget::ResultWidget( bool satisfied, if ( satisfied ) setCondition( m_iconLabel, CalamaresUtils::StatusOk ); + else if ( required ) + setCondition( m_iconLabel, CalamaresUtils::StatusError ); else - if ( required ) - setCondition( m_iconLabel, CalamaresUtils::StatusError ); - else - setCondition( m_iconLabel, CalamaresUtils::StatusWarning ); + setCondition( m_iconLabel, CalamaresUtils::StatusWarning ); } diff --git a/src/modules/welcome/checker/ResultsListWidget.cpp b/src/modules/welcome/checker/ResultsListWidget.cpp index ca1a9459a..5908e4bf5 100644 --- a/src/modules/welcome/checker/ResultsListWidget.cpp +++ b/src/modules/welcome/checker/ResultsListWidget.cpp @@ -1,7 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac - * Copyright 2017, Adriaan de Groot + * Copyright 2017, 2019, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -69,9 +69,7 @@ ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) allChecked = false; if ( entry.mandatory ) - { requirementsSatisfied = false; - } ciw->setAutoFillBackground( true ); QPalette pal( ciw->palette() ); pal.setColor( QPalette::Background, Qt::white ); @@ -97,7 +95,7 @@ ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) "requirements for installing %1.
" "Installation cannot continue. " "Details..." ) - .arg( *Calamares::Branding::ShortVersionedName ) ); + .arg( *Calamares::Branding::ShortVersionedName ) ); ) textLabel->setOpenExternalLinks( false ); connect( textLabel, &QLabel::linkActivated, @@ -114,7 +112,7 @@ ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) "recommended requirements for installing %1.
" "Installation can continue, but some features " "might be disabled." ) - .arg( *Calamares::Branding::ShortVersionedName ) ); + .arg( *Calamares::Branding::ShortVersionedName ) ); ) } } @@ -122,16 +120,16 @@ ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) if ( allChecked && requirementsSatisfied ) { if ( !Calamares::Branding::instance()-> - imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) + imagePath( Calamares::Branding::ProductWelcome ).isEmpty() ) { QPixmap theImage = QPixmap( Calamares::Branding::instance()-> - imagePath( Calamares::Branding::ProductWelcome ) ); + imagePath( Calamares::Branding::ProductWelcome ) ); if ( !theImage.isNull() ) { QLabel* imageLabel; if ( Calamares::Branding::instance()->welcomeExpandingLogo() ) { - FixedAspectRatioLabel *p = new FixedAspectRatioLabel; + FixedAspectRatioLabel* p = new FixedAspectRatioLabel; p->setPixmap( theImage ); imageLabel = p; } @@ -155,9 +153,7 @@ ResultsListWidget::init( const Calamares::RequirementsList& checkEntries ) ) } else - { m_mainLayout->addStretch(); - } } @@ -194,8 +190,8 @@ ResultsListWidget::showDetailsDialog( const Calamares::RequirementsList& checkEn } QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close, - Qt::Horizontal, - this ); + Qt::Horizontal, + this ); mainLayout->addWidget( buttonBox ); detailsDialog->setModal( true ); diff --git a/src/modules/welcome/checker/ResultsListWidget.h b/src/modules/welcome/checker/ResultsListWidget.h index 58bac0602..3be02b0d0 100644 --- a/src/modules/welcome/checker/ResultsListWidget.h +++ b/src/modules/welcome/checker/ResultsListWidget.h @@ -1,6 +1,7 @@ /* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac + * Copyright 2019, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 616515cce557a6f64618268d0ae0eeb97255d21f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 26 Feb 2019 06:34:56 -0500 Subject: [PATCH 6/6] Changes: document requirements-checker - Name contributors - Document changes in requirements-checking - Document libparted no-longer-needed --- CHANGES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index b0a823883..d68a66816 100644 --- a/CHANGES +++ b/CHANGES @@ -6,16 +6,29 @@ website will have to do for older versions. # 3.2.5 (unreleased) # This release contains contributions from (alphabetically by first name): + - Arnaud Ferraris + - Dan Simmons ## Core ## + * View modules (in C++) can now perform their own requirements-checking + to see if installation makes sense. This expands upon the existing + requirements checks in the welcome module (RAM, disk space, ..). + The checks have been made asynchronous, so that responsiveness during + requirements-checking is improved and the user has better feedback. + ## Modules ## + * *Partition* module: it is now possible to build without libparted. Since + KPMCore may not need this library anymore, it is a dependency that will + be dropped as soon as it is feasible. Add `-DCMAKE_DISABLE_FIND_PACKAGE_LIBPARTED=ON` + to the CMake flags to do so. * Python modules: several modules have had translations added. This is usually only visible when the module runs as part of the *exec* step, when the module's *pretty name* is displayed. In addition, error messages are now translated. + # 3.2.4 (2019-02-12) # This release contains contributions from (alphabetically by first name):