diff --git a/src/modules/finished/CMakeLists.txt b/src/modules/finished/CMakeLists.txt index 21eb1ad18..b4d59db8f 100644 --- a/src/modules/finished/CMakeLists.txt +++ b/src/modules/finished/CMakeLists.txt @@ -11,6 +11,7 @@ calamares_add_plugin( finished TYPE viewmodule EXPORT_MACRO PLUGINDLLEXPORT_PRO SOURCES + Config.cpp FinishedViewStep.cpp FinishedPage.cpp UI diff --git a/src/modules/finished/Config.cpp b/src/modules/finished/Config.cpp new file mode 100644 index 000000000..5119da942 --- /dev/null +++ b/src/modules/finished/Config.cpp @@ -0,0 +1,201 @@ +/* === 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 "Branding.h" +#include "Settings.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include +#include +#include +#include + +const NamedEnumTable< Config::RestartMode >& +restartModes() +{ + using M = Config::RestartMode; + static const NamedEnumTable< M > table { { "never", M::Never }, + { "user-unchecked", M::UserDefaultUnchecked }, + { "unchecked", M::UserDefaultUnchecked }, + { "user-checked", M::UserDefaultChecked }, + { "checked", M::UserDefaultChecked }, + { "always", M::Always } + + }; + return table; +} + + +Config::Config( QObject* parent ) + : QObject( parent ) +{ +} + +void +Config::setRestartNowMode( Config::RestartMode m ) +{ + // Can only go "down" in state (Always > UserDefaultChecked > .. > Never) + if ( m > m_restartNowMode ) + { + return; + } + + // If changing to an unconditional mode, also set other flag + if ( m == RestartMode::Always || m == RestartMode::Never ) + { + setRestartNowWanted( m == RestartMode::Always ); + } + + if ( m != m_restartNowMode ) + { + m_restartNowMode = m; + emit restartModeChanged( m ); + } +} + +void +Config::setRestartNowWanted( bool w ) +{ + // Follow the mode which may affect @p w + if ( m_restartNowMode == RestartMode::Always ) + { + w = true; + } + if ( m_restartNowMode == RestartMode::Never ) + { + w = false; + } + + if ( w != m_userWantsRestart ) + { + m_userWantsRestart = w; + emit restartNowWantedChanged( w ); + } +} + +void +Config::doRestart() +{ + if ( restartNowMode() != RestartMode::Never && restartNowWanted() ) + { + cDebug() << "Running restart command" << m_restartNowCommand; + QProcess::execute( "/bin/sh", { "-c", m_restartNowCommand } ); + } +} + + +void +Config::doNotify( bool hasFailed ) +{ + if ( !notifyOnFinished() ) + { + return; + } + + QDBusInterface notify( + "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications" ); + if ( notify.isValid() ) + { + cDebug() << "Sending notification of completion. Failed?" << hasFailed; + + QString title; + QString message; + if ( hasFailed ) + { + title = Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Failed" ) : tr( "Installation Failed" ); + message = Calamares::Settings::instance()->isSetupMode() + ? tr( "The setup of %1 did not complete successfully." ) + : tr( "The installation of %1 did not complete successfully." ); + } + else + { + title = Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Complete" ) + : tr( "Installation Complete" ); + message = Calamares::Settings::instance()->isSetupMode() ? tr( "The setup of %1 is complete." ) + : tr( "The installation of %1 is complete." ); + } + + const auto* branding = Calamares::Branding::instance(); + QDBusReply< uint > r = notify.call( "Notify", + QString( "Calamares" ), + QVariant( 0U ), + QString( "calamares" ), + title, + message.arg( branding->versionedName() ), + QStringList(), + QVariantMap(), + QVariant( 0 ) ); + if ( !r.isValid() ) + { + cWarning() << "Could not call org.freedesktop.Notifications.Notify at end of installation." << r.error(); + } + } + else + { + cWarning() << "Could not get dbus interface for notifications at end of installation." << notify.lastError(); + } +} + + +void +Config::setConfigurationMap( const QVariantMap& configurationMap ) +{ + RestartMode mode = RestartMode::Never; + + //TODO:3.3 remove deprecated restart settings + QString restartMode = CalamaresUtils::getString( configurationMap, "restartNowMode" ); + if ( restartMode.isEmpty() ) + { + if ( configurationMap.contains( "restartNowEnabled" ) ) + { + cWarning() << "Configuring the finished module with deprecated restartNowEnabled settings"; + } + + bool restartNowEnabled = CalamaresUtils::getBool( configurationMap, "restartNowEnabled", false ); + bool restartNowChecked = CalamaresUtils::getBool( configurationMap, "restartNowChecked", false ); + + if ( !restartNowEnabled ) + { + mode = RestartMode::Never; + } + else + { + mode = restartNowChecked ? RestartMode::UserDefaultChecked : RestartMode::UserDefaultUnchecked; + } + } + else + { + bool ok = false; + mode = restartModes().find( restartMode, ok ); + if ( !ok ) + { + cWarning() << "Configuring the finished module with bad restartNowMode" << restartMode; + } + } + + m_restartNowMode = mode; + m_userWantsRestart = ( mode == RestartMode::Always || mode == RestartMode::UserDefaultChecked ); + emit restartModeChanged( m_restartNowMode ); + emit restartNowWantedChanged( m_userWantsRestart ); + + if ( mode != RestartMode::Never ) + { + QString restartNowCommand = CalamaresUtils::getString( configurationMap, "restartNowCommand" ); + if ( restartNowCommand.isEmpty() ) + { + restartNowCommand = QStringLiteral( "shutdown -r now" ); + } + m_restartNowCommand = restartNowCommand; + } + + m_notifyOnFinished = CalamaresUtils::getBool( configurationMap, "notifyOnFinished", false ); +} diff --git a/src/modules/finished/Config.h b/src/modules/finished/Config.h new file mode 100644 index 000000000..78078b99b --- /dev/null +++ b/src/modules/finished/Config.h @@ -0,0 +1,81 @@ +/* === 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. + * + */ + +#ifndef FINISHED_CONFIG_H +#define FINISHED_CONFIG_H + +#include "utils/NamedEnum.h" + +#include + +class Config : public QObject +{ + Q_OBJECT + + Q_PROPERTY( RestartMode restartNowMode READ restartNowMode WRITE setRestartNowMode NOTIFY restartModeChanged ) + Q_PROPERTY( bool restartNowWanted READ restartNowWanted WRITE setRestartNowWanted NOTIFY restartNowWantedChanged ) + + Q_PROPERTY( QString restartNowCommand READ restartNowCommand CONSTANT FINAL ) + Q_PROPERTY( bool notifyOnFinished READ notifyOnFinished CONSTANT FINAL ) + +public: + Config( QObject* parent = nullptr ); + + enum class RestartMode + { + Never, + UserDefaultUnchecked, + UserDefaultChecked, + Always + }; + Q_ENUM( RestartMode ) + + RestartMode restartNowMode() const { return m_restartNowMode; } + bool restartNowWanted() const { return m_userWantsRestart; } + + QString restartNowCommand() const { return m_restartNowCommand; } + bool notifyOnFinished() const { return m_notifyOnFinished; } + + void setConfigurationMap( const QVariantMap& configurationMap ); + +public slots: + void setRestartNowMode( RestartMode m ); + void setRestartNowWanted( bool w ); + + /** @brief Run the restart command, if desired. + * + * This should generally not be called somewhere during the + * application's execution, but only in response to QApplication::quit() + * or something like that when the user expects the system to restart. + */ + void doRestart(); + + /** @brief Send DBus notification, if desired. + * + * This takes notifyOnFinished() into account. + * + * At the end of installation (when the FinishedViewStep is activated), + * send a desktop notification via DBus that the install is done. + */ + void doNotify( bool hasFailed = false ); + +signals: + void restartModeChanged( RestartMode m ); + void restartNowWantedChanged( bool w ); + +private: + QString m_restartNowCommand; + RestartMode m_restartNowMode = RestartMode::Never; + bool m_userWantsRestart = false; + bool m_notifyOnFinished = false; +}; + +const NamedEnumTable< Config::RestartMode >& restartModes(); + +#endif diff --git a/src/modules/finished/FinishedPage.cpp b/src/modules/finished/FinishedPage.cpp index 23f09df99..6c5f9ad16 100644 --- a/src/modules/finished/FinishedPage.cpp +++ b/src/modules/finished/FinishedPage.cpp @@ -11,26 +11,19 @@ #include "FinishedPage.h" -#include "CalamaresVersion.h" -#include "ViewManager.h" +#include "Config.h" #include "ui_FinishedPage.h" -#include "utils/CalamaresUtilsGui.h" -#include "utils/Logger.h" + +#include "Branding.h" +#include "Settings.h" #include "utils/Retranslator.h" -#include -#include #include -#include -#include -#include "Branding.h" -#include "Settings.h" -FinishedPage::FinishedPage( QWidget* parent ) +FinishedPage::FinishedPage( Config* config, QWidget* parent ) : QWidget( parent ) , ui( new Ui::FinishedPage ) - , m_mode( FinishedViewStep::RestartMode::UserUnchecked ) { ui->setupUi( this ); @@ -38,9 +31,44 @@ FinishedPage::FinishedPage( QWidget* parent ) ui->mainText->setWordWrap( true ); ui->mainText->setOpenExternalLinks( true ); - CALAMARES_RETRANSLATE( - const auto* branding = Calamares::Branding::instance(); ui->retranslateUi( this ); - if ( Calamares::Settings::instance()->isSetupMode() ) { + connect( config, &Config::restartModeChanged, [this]( Config::RestartMode mode ) { + using Mode = Config::RestartMode; + + ui->restartCheckBox->setVisible( mode != Mode::Never ); + ui->restartCheckBox->setEnabled( mode != Mode::Always ); + } ); + connect( config, &Config::restartNowWantedChanged, ui->restartCheckBox, &QCheckBox::setChecked ); + connect( ui->restartCheckBox, &QCheckBox::stateChanged, [config]( int state ) { + config->setRestartNowWanted( state != 0 ); + } ); + + CALAMARES_RETRANSLATE_SLOT( &FinishedPage::retranslate ); +} + +void +FinishedPage::focusInEvent( QFocusEvent* e ) +{ + e->accept(); +} + +void +FinishedPage::onInstallationFailed( const QString& message, const QString& details ) +{ + m_failure = !message.isEmpty() ? message : details; + retranslate(); +} + +void +FinishedPage::retranslate() +{ + + const auto* branding = Calamares::Branding::instance(); + + ui->retranslateUi( this ); + if ( !m_failure.has_value() ) + { + if ( Calamares::Settings::instance()->isSetupMode() ) + { ui->mainText->setText( tr( "

All done.


" "%1 has been set up on your computer.
" "You may now start using your new system." ) @@ -50,7 +78,9 @@ FinishedPage::FinishedPage( QWidget* parent ) "restart immediately when you click on " "Done " "or close the setup program.

" ) ); - } else { + } + else + { ui->mainText->setText( tr( "

All done.


" "%1 has been installed on your computer.
" "You may now restart into your new system, or continue " @@ -61,68 +91,27 @@ FinishedPage::FinishedPage( QWidget* parent ) "restart immediately when you click on " "Done " "or close the installer.

" ) ); - } ) -} - - -void -FinishedPage::setRestart( FinishedViewStep::RestartMode mode ) -{ - using Mode = FinishedViewStep::RestartMode; - - m_mode = mode; - - ui->restartCheckBox->setVisible( mode != Mode::Never ); - ui->restartCheckBox->setEnabled( mode != Mode::Always ); - ui->restartCheckBox->setChecked( ( mode == Mode::Always ) || ( mode == Mode::UserChecked ) ); -} - - -void -FinishedPage::setRestartNowCommand( const QString& command ) -{ - m_restartNowCommand = command; -} - - -void -FinishedPage::setUpRestart() -{ - cDebug() << "FinishedPage::setUpRestart(), Quit button" - << "setup=" << FinishedViewStep::modeName( m_mode ) << "command=" << m_restartNowCommand; + } + } + else + { + const QString message = m_failure.value(); - connect( qApp, &QApplication::aboutToQuit, [this]() { - if ( ui->restartCheckBox->isVisible() && ui->restartCheckBox->isChecked() ) + if ( Calamares::Settings::instance()->isSetupMode() ) { - cDebug() << "Running restart command" << m_restartNowCommand; - QProcess::execute( "/bin/sh", { "-c", m_restartNowCommand } ); + ui->mainText->setText( tr( "

Setup Failed


" + "%1 has not been set up on your computer.
" + "The error message was: %2." ) + .arg( branding->versionedName() ) + .arg( message ) ); } - } ); -} - - -void -FinishedPage::focusInEvent( QFocusEvent* e ) -{ - e->accept(); -} - -void -FinishedPage::onInstallationFailed( const QString& message, const QString& details ) -{ - const auto* branding = Calamares::Branding::instance(); - Q_UNUSED( details ) - if ( Calamares::Settings::instance()->isSetupMode() ) - ui->mainText->setText( tr( "

Setup Failed


" - "%1 has not been set up on your computer.
" - "The error message was: %2." ) - .arg( branding->versionedName() ) - .arg( message ) ); - else - ui->mainText->setText( tr( "

Installation Failed


" - "%1 has not been installed on your computer.
" - "The error message was: %2." ) - .arg( branding->versionedName() ) - .arg( message ) ); - setRestart( FinishedViewStep::RestartMode::Never ); + else + { + ui->mainText->setText( tr( "

Installation Failed


" + "%1 has not been installed on your computer.
" + "The error message was: %2." ) + .arg( branding->versionedName() ) + .arg( message ) ); + } + } } diff --git a/src/modules/finished/FinishedPage.h b/src/modules/finished/FinishedPage.h index 1df022f58..068efbdb5 100644 --- a/src/modules/finished/FinishedPage.h +++ b/src/modules/finished/FinishedPage.h @@ -11,10 +11,12 @@ #ifndef FINISHEDPAGE_H #define FINISHEDPAGE_H + #include -#include "FinishedViewStep.h" +#include +class Config; namespace Ui { class FinishedPage; @@ -24,24 +26,19 @@ class FinishedPage : public QWidget { Q_OBJECT public: - explicit FinishedPage( QWidget* parent = nullptr ); - - void setRestart( FinishedViewStep::RestartMode mode ); - void setRestartNowCommand( const QString& command ); + explicit FinishedPage( Config* config, QWidget* parent = nullptr ); - void setUpRestart(); public slots: void onInstallationFailed( const QString& message, const QString& details ); + void retranslate(); protected: void focusInEvent( QFocusEvent* e ) override; //choose the child widget to focus private: Ui::FinishedPage* ui; - - FinishedViewStep::RestartMode m_mode; - QString m_restartNowCommand; + std::optional< QString > m_failure; }; #endif // FINISHEDPAGE_H diff --git a/src/modules/finished/FinishedViewStep.cpp b/src/modules/finished/FinishedViewStep.cpp index 525108dc7..3f9fd3aab 100644 --- a/src/modules/finished/FinishedViewStep.cpp +++ b/src/modules/finished/FinishedViewStep.cpp @@ -10,42 +10,21 @@ */ #include "FinishedViewStep.h" + +#include "Config.h" #include "FinishedPage.h" -#include "Branding.h" #include "JobQueue.h" -#include "Settings.h" - -#include "utils/Logger.h" -#include "utils/NamedEnum.h" -#include "utils/Variant.h" - -#include -#include -#include -#include - -static const NamedEnumTable< FinishedViewStep::RestartMode >& -modeNames() -{ - using Mode = FinishedViewStep::RestartMode; - static const NamedEnumTable< Mode > names { { QStringLiteral( "never" ), Mode::Never }, - { QStringLiteral( "user-unchecked" ), Mode::UserUnchecked }, - { QStringLiteral( "user-checked" ), Mode::UserChecked }, - { QStringLiteral( "always" ), Mode::Always } }; - - return names; -} +#include FinishedViewStep::FinishedViewStep( QObject* parent ) : Calamares::ViewStep( parent ) - , m_widget( new FinishedPage() ) - , installFailed( false ) - , m_notifyOnFinished( false ) + , m_config( new Config( this ) ) + , m_widget( new FinishedPage( m_config ) ) + , m_installFailed( false ) { auto jq = Calamares::JobQueue::instance(); - connect( jq, &Calamares::JobQueue::failed, m_widget, &FinishedPage::onInstallationFailed ); connect( jq, &Calamares::JobQueue::failed, this, &FinishedViewStep::onInstallationFailed ); emit nextStatusChanged( true ); @@ -102,54 +81,12 @@ FinishedViewStep::isAtEnd() const return true; } -void -FinishedViewStep::sendNotification() -{ - // If the installation failed, don't send notification popup; - // there's a (modal) dialog popped up with the failure notice. - if ( installFailed ) - { - return; - } - - QDBusInterface notify( - "org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications" ); - if ( notify.isValid() ) - { - const auto* branding = Calamares::Branding::instance(); - QDBusReply< uint > r = notify.call( - "Notify", - QString( "Calamares" ), - QVariant( 0U ), - QString( "calamares" ), - Calamares::Settings::instance()->isSetupMode() ? tr( "Setup Complete" ) : tr( "Installation Complete" ), - Calamares::Settings::instance()->isSetupMode() - ? tr( "The setup of %1 is complete." ).arg( branding->versionedName() ) - : tr( "The installation of %1 is complete." ).arg( branding->versionedName() ), - QStringList(), - QVariantMap(), - QVariant( 0 ) ); - if ( !r.isValid() ) - { - cWarning() << "Could not call org.freedesktop.Notifications.Notify at end of installation." << r.error(); - } - } - else - { - cWarning() << "Could not get dbus interface for notifications at end of installation." << notify.lastError(); - } -} - void FinishedViewStep::onActivate() { - m_widget->setUpRestart(); - - if ( m_notifyOnFinished ) - { - sendNotification(); - } + m_config->doNotify( m_installFailed ); + connect( qApp, &QApplication::aboutToQuit, m_config, &Config::doRestart ); } @@ -162,67 +99,15 @@ FinishedViewStep::jobs() const void FinishedViewStep::onInstallationFailed( const QString& message, const QString& details ) { - Q_UNUSED( message ) - Q_UNUSED( details ) - installFailed = true; + m_installFailed = true; + m_config->setRestartNowMode( Config::RestartMode::Never ); + m_widget->onInstallationFailed( message, details ); } void FinishedViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { - RestartMode mode = RestartMode::Never; - - QString restartMode = CalamaresUtils::getString( configurationMap, "restartNowMode" ); - if ( restartMode.isEmpty() ) - { - if ( configurationMap.contains( "restartNowEnabled" ) ) - { - cWarning() << "Configuring the finished module with deprecated restartNowEnabled settings"; - } - - bool restartNowEnabled = CalamaresUtils::getBool( configurationMap, "restartNowEnabled", false ); - bool restartNowChecked = CalamaresUtils::getBool( configurationMap, "restartNowChecked", false ); - - if ( !restartNowEnabled ) - { - mode = RestartMode::Never; - } - else - { - mode = restartNowChecked ? RestartMode::UserChecked : RestartMode::UserUnchecked; - } - } - else - { - bool ok = false; - mode = modeNames().find( restartMode, ok ); - if ( !ok ) - { - cWarning() << "Configuring the finished module with bad restartNowMode" << restartMode; - } - } - - m_widget->setRestart( mode ); - - if ( mode != RestartMode::Never ) - { - QString restartNowCommand = CalamaresUtils::getString( configurationMap, "restartNowCommand" ); - if ( restartNowCommand.isEmpty() ) - { - restartNowCommand = QStringLiteral( "shutdown -r now" ); - } - m_widget->setRestartNowCommand( restartNowCommand ); - } - - m_notifyOnFinished = CalamaresUtils::getBool( configurationMap, "notifyOnFinished", false ); -} - -QString -FinishedViewStep::modeName( FinishedViewStep::RestartMode m ) -{ - bool ok = false; - return modeNames().find( m, ok ); // May be QString() + m_config->setConfigurationMap( configurationMap ); } - CALAMARES_PLUGIN_FACTORY_DEFINITION( FinishedViewStepFactory, registerPlugin< FinishedViewStep >(); ) diff --git a/src/modules/finished/FinishedViewStep.h b/src/modules/finished/FinishedViewStep.h index 729f9126d..a35d7fac8 100644 --- a/src/modules/finished/FinishedViewStep.h +++ b/src/modules/finished/FinishedViewStep.h @@ -11,13 +11,11 @@ #ifndef FINISHEDVIEWSTEP_H #define FINISHEDVIEWSTEP_H -#include - +#include "DllMacro.h" #include "utils/PluginFactory.h" #include "viewpages/ViewStep.h" -#include "DllMacro.h" - +class Config; class FinishedPage; class PLUGINDLLEXPORT FinishedViewStep : public Calamares::ViewStep @@ -25,16 +23,6 @@ class PLUGINDLLEXPORT FinishedViewStep : public Calamares::ViewStep Q_OBJECT public: - enum class RestartMode - { - Never = 0, ///< @brief Don't show button, just exit - UserUnchecked, ///< @brief Show button, starts unchecked - UserChecked, ///< @brief Show button, starts checked - Always ///< @brief Show button, can't change, checked - }; - /// @brief Returns the config-name of the given restart-mode @p m - static QString modeName( RestartMode m ); - explicit FinishedViewStep( QObject* parent = nullptr ); ~FinishedViewStep() override; @@ -58,16 +46,9 @@ public slots: void onInstallationFailed( const QString& message, const QString& details ); private: + Config* m_config; FinishedPage* m_widget; - - /** - * @brief At the end of installation (when this step is activated), - * send a desktop notification via DBus that the install is done. - */ - void sendNotification(); - - bool installFailed; - bool m_notifyOnFinished; + bool m_installFailed; // Track if onInstallationFailed() was called }; CALAMARES_PLUGIN_FACTORY_DECLARATION( FinishedViewStepFactory )