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

This commit is contained in:
Philip Müller 2019-02-26 18:15:19 +01:00
commit afa70e1944
54 changed files with 928 additions and 543 deletions

13
CHANGES
View File

@ -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):

View File

@ -348,6 +348,7 @@ void
CalamaresApplication::initViewSteps()
{
cDebug() << "STARTUP: loadModules for all modules done";
m_moduleManager->checkRequirements();
if ( Calamares::Branding::instance()->windowMaximize() )
{
m_mainwindow->setWindowFlag( Qt::FramelessWindowHint );
@ -355,6 +356,7 @@ CalamaresApplication::initViewSteps()
}
else
m_mainwindow->show();
ProgressTreeModel* m = new ProgressTreeModel( nullptr );
ProgressTreeView::instance()->setModel( m );
cDebug() << "STARTUP: Window now visible and ProgressTreeView populated";

View File

@ -5,6 +5,8 @@ set( calamaresui_SOURCES
modulesystem/Module.cpp
modulesystem/ModuleManager.cpp
modulesystem/ProcessJobModule.cpp
modulesystem/Requirement.cpp
modulesystem/RequirementsChecker.cpp
modulesystem/ViewModule.cpp
utils/CalamaresUtilsGui.cpp

View File

@ -281,4 +281,10 @@ Module::initFrom( const QVariantMap& moduleDescriptor )
m_maybe_emergency = moduleDescriptor[ EMERGENCY ].toBool();
}
RequirementsList
Module::checkRequirements()
{
return RequirementsList();
}
} //ns

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,6 +20,7 @@
#ifndef CALAMARES_MODULE_H
#define CALAMARES_MODULE_H
#include "Requirement.h"
#include "UiDllMacro.h"
#include <Typedefs.h>
@ -178,6 +180,11 @@ public:
*/
QVariantMap configurationMap();
/**
* @brief Check the requirements of this module.
*/
virtual RequirementsList checkRequirements();
protected:
explicit Module();
virtual void initFrom( const QVariantMap& moduleDescriptor );

View File

@ -21,26 +21,23 @@
#include "ExecutionViewStep.h"
#include "Module.h"
#include "utils/Logger.h"
#include "utils/YamlUtils.h"
#include "RequirementsChecker.h"
#include "Settings.h"
#include "ViewManager.h"
#include "utils/Logger.h"
#include "utils/YamlUtils.h"
#include <yaml-cpp/yaml.h>
#include <QApplication>
#include <QDir>
#include <QTimer>
#define MODULE_CONFIG_FILENAME "module.desc"
namespace Calamares
{
ModuleManager* ModuleManager::s_instance = nullptr;
ModuleManager*
ModuleManager::instance()
{
@ -94,7 +91,7 @@ ModuleManager::doInit()
bool success = currentDir.cd( subdir );
if ( success )
{
QFileInfo descriptorFileInfo( currentDir.absoluteFilePath( MODULE_CONFIG_FILENAME ) );
QFileInfo descriptorFileInfo( currentDir.absoluteFilePath( QLatin1Literal( "module.desc") ) );
if ( ! ( descriptorFileInfo.exists() && descriptorFileInfo.isReadable() ) )
{
cDebug() << Q_FUNC_INFO << "unreadable file: "
@ -307,6 +304,26 @@ ModuleManager::loadModules()
} );
}
void
ModuleManager::checkRequirements()
{
cDebug() << "Checking module requirements ..";
QVector< Module* > modules( m_loadedModulesByInstanceKey.count() );
int count = 0;
for (const auto& module : m_loadedModulesByInstanceKey )
{
modules[count++] = module;
}
RequirementsChecker *rq = new RequirementsChecker( modules, this );
connect( rq, &RequirementsChecker::requirementsResult, this, &ModuleManager::requirementsResult );
connect( rq, &RequirementsChecker::requirementsComplete, this, &ModuleManager::requirementsComplete );
connect( rq, &RequirementsChecker::requirementsProgress, this, &ModuleManager::requirementsProgress );
connect( rq, &RequirementsChecker::done, rq, &RequirementsChecker::deleteLater );
QTimer::singleShot( 0, rq, &RequirementsChecker::run );
}
QStringList
ModuleManager::checkDependencies()
@ -334,8 +351,6 @@ ModuleManager::checkDependencies()
break;
}
}
if ( somethingWasRemovedBecauseOfUnmetDependencies )
break;
}
if ( !somethingWasRemovedBecauseOfUnmetDependencies )
break;
@ -369,4 +384,4 @@ ModuleManager::checkDependencies( const Module& m )
return allRequirementsFound;
}
}
} // namespace

View File

@ -20,6 +20,7 @@
#ifndef MODULELOADER_H
#define MODULELOADER_H
#include "Requirement.h"
#include "Typedefs.h"
#include <QObject>
@ -30,6 +31,7 @@ namespace Calamares
{
class Module;
struct RequirementEntry; // from Requirement.h
/**
* @brief The ModuleManager class is a singleton which manages Calamares modules.
@ -81,10 +83,20 @@ public:
*/
void loadModules();
/**
* @brief Starts asynchronous requirements checking for each module.
* When this is done, the signal modulesChecked is emitted.
*/
void checkRequirements();
signals:
void initDone();
void modulesLoaded(); /// All of the modules were loaded successfully
void modulesFailed( QStringList ); /// .. or not
// Below, see RequirementsChecker documentation
void requirementsComplete( bool );
void requirementsResult( RequirementsList );
void requirementsProgress( const QString& );
private slots:
void doInit();

View File

@ -1,6 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
@ -16,23 +15,5 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Requirement.h"
#ifndef CHECKITEMWIDGET_H
#define CHECKITEMWIDGET_H
#include <QLabel>
class CheckItemWidget : public QWidget
{
Q_OBJECT
public:
explicit CheckItemWidget( bool checked, bool required,
QWidget* parent = nullptr );
void setText( const QString& text );
private:
QLabel* m_textLabel;
QLabel* m_iconLabel;
};
#endif // CHECKITEMWIDGET_H

View File

@ -0,0 +1,67 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2017, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALAMARES_REQUIREMENT_H
#define CALAMARES_REQUIREMENT_H
#include <QList>
#include <QMetaType>
#include <QString>
#include <functional>
namespace Calamares
{
/**
* An indication of a requirement, which is checked in preparation
* for system installation. An entry has a name and some explanation functions
* (functions, because they need to respond to translations).
*
* A requirement can be *satisfied* or not.
* A requirement can be optional (i.e. a "good to have") or mandatory.
*
* Requirements which are not satisfied, and also mandatory, will prevent the
* installation from proceeding.
*/
struct RequirementEntry
{
using TextFunction = std::function< QString() >;
/// @brief name of this requirement; not shown to user and used as ID
QString name;
/// @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, 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 >;
} // namespace Calamares
Q_DECLARE_METATYPE(Calamares::RequirementEntry)
#endif

View File

@ -0,0 +1,154 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "RequirementsChecker.h"
#include "Module.h"
#include "Requirement.h"
#include "utils/Logger.h"
#include <algorithm>
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
#include <QFutureWatcher>
#include <QTimer>
namespace Calamares
{
static void
registerMetatypes()
{
static bool done = false;
if ( !done )
{
qRegisterMetaType< RequirementEntry >( "RequirementEntry" );
// It's sensitive to the names of types in parameters; in particular
// althrough QList<RequirementEntry> is the same as RequirementsList,
// because we *name* the type as RequirementsList in the parameters,
// we need to register that (as well). Here, be safe and register
// both names.
qRegisterMetaType< QList< RequirementEntry > >( "QList<RequirementEntry>" );
qRegisterMetaType< RequirementsList >( "RequirementsList" );
done = true;
}
}
static void
check( Module * const &m, RequirementsChecker *c )
{
RequirementsList l = m->checkRequirements();
if ( l.count() > 0 )
c->addCheckedRequirements( l );
c->requirementsProgress( QObject::tr( "Requirements checking for module <i>%1</i> is complete." ).arg( m->name() ) );
}
RequirementsChecker::RequirementsChecker( QVector< Module* > modules, QObject* parent )
: QObject( parent )
, m_modules( std::move( modules ) )
, m_progressTimer( nullptr )
, m_progressTimeouts( 0 )
{
m_watchers.reserve( m_modules.count() );
m_collectedRequirements.reserve( m_modules.count() );
registerMetatypes();
}
RequirementsChecker::~RequirementsChecker()
{
}
void
RequirementsChecker::run()
{
m_progressTimer = new QTimer( this );
connect( m_progressTimer, &QTimer::timeout, this, &RequirementsChecker::reportProgress );
m_progressTimer->start( 1200 ); // msec
for (const auto& module : m_modules )
{
Watcher *watcher = new Watcher( this );
watcher->setFuture( QtConcurrent::run( check, module, this ) );
m_watchers.append( watcher );
connect( watcher, &Watcher::finished, this, &RequirementsChecker::finished );
}
QTimer::singleShot( 0, this, &RequirementsChecker::finished );
}
void
RequirementsChecker::finished()
{
if ( std::all_of( m_watchers.cbegin(), m_watchers.cend(), []( const Watcher *w ) { return w && w->isFinished(); } ) )
{
cDebug() << "All requirements have been checked.";
if ( m_progressTimer )
m_progressTimer->stop();
bool acceptable = true;
int count = 0;
for ( const auto& r : m_collectedRequirements )
{
if ( r.mandatory && !r.satisfied )
{
cDebug() << " .. requirement" << count << r.name << "is not satisfied.";
acceptable = false;
}
++count;
}
emit requirementsComplete( acceptable );
QTimer::singleShot(0, this, &RequirementsChecker::done );
}
}
void
RequirementsChecker::addCheckedRequirements( RequirementsList l )
{
static QMutex addMutex;
{
QMutexLocker lock( &addMutex );
m_collectedRequirements.append( l );
}
cDebug() << "Added" << l.count() << "requirement results";
emit requirementsResult( l );
}
void
RequirementsChecker::reportProgress()
{
m_progressTimeouts++;
auto remaining = std::count_if( m_watchers.cbegin(), m_watchers.cend(), []( const Watcher *w ) { return w && !w->isFinished(); } );
if ( remaining > 0 )
{
QString waiting = tr( "Waiting for %n module(s).", "", remaining );
QString elapsed = tr( "(%n second(s))", "", m_progressTimeouts * m_progressTimer->interval() / 1000 );
emit requirementsProgress( waiting + QString( " " ) + elapsed );
}
else
emit requirementsProgress( tr( "System-requirements checking is complete." ) );
}
}

View File

@ -0,0 +1,87 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CALAMARES_REQUIREMENTSCHECKER_H
#define CALAMARES_REQUIREMENTSCHECKER_H
#include "Requirement.h"
#include <QFutureWatcher>
#include <QObject>
#include <QTimer>
#include <QVector>
namespace Calamares
{
class Module;
/** @brief A manager-class that checks all the module requirements
*
* Asynchronously checks the requirements for each module, and
* emits progress signals as appropriate.
*/
class RequirementsChecker : public QObject
{
Q_OBJECT
public:
RequirementsChecker( QVector< Module* > modules, QObject* parent = nullptr );
virtual ~RequirementsChecker() override;
public slots:
/// @brief Start checking all the requirements
void run();
/// @brief Called when requirements are reported by a module
void addCheckedRequirements( RequirementsList );
/// @brief Called when all requirements have been checked
void finished();
/// @brief Called periodically while requirements are being checked
void reportProgress();
signals:
/// @brief Human-readable progress message
void requirementsProgress( const QString& );
/// @brief Requirements from a single module
void requirementsResult( RequirementsList );
/** @brief When all requirements are collected
*
* The argument indicates if all mandatory requirements are satisfied.
*/
void requirementsComplete( bool );
/// @brief Emitted after requirementsComplete
void done();
private:
QVector< Module* > m_modules;
using Watcher = QFutureWatcher< void >;
QVector< Watcher* > m_watchers;
RequirementsList m_collectedRequirements;
QTimer *m_progressTimer;
unsigned m_progressTimeouts;
} ;
}
#endif

View File

@ -132,4 +132,10 @@ ViewModule::~ViewModule()
delete m_loader;
}
RequirementsList
ViewModule::checkRequirements()
{
return m_viewStep->checkRequirements();
}
} // namespace Calamares

View File

@ -39,6 +39,8 @@ public:
void loadSelf() override;
JobList jobs() const override;
RequirementsList checkRequirements() override;
protected:
void initFrom( const QVariantMap& moduleDescriptor ) override;

View File

@ -52,6 +52,14 @@ void
ViewStep::onLeave()
{}
void
ViewStep::next()
{}
void
ViewStep::back()
{}
void
ViewStep::setModuleInstanceKey( const QString& instanceKey )
@ -66,4 +74,10 @@ ViewStep::setConfigurationMap( const QVariantMap& configurationMap )
Q_UNUSED( configurationMap );
}
RequirementsList ViewStep::checkRequirements()
{
return RequirementsList();
}
}

View File

@ -22,6 +22,7 @@
#include <QObject>
#include "modulesystem/Requirement.h"
#include "../UiDllMacro.h"
#include "Typedefs.h"
@ -68,13 +69,39 @@ public:
//TODO: we might want to make this a QSharedPointer
virtual QWidget* widget() = 0;
virtual void next() = 0;
virtual void back() = 0;
/**
* @brief Multi-page support, go next
*
* Multi-page view steps need to manage the content visible in the widget
* themselves. This method is called when the user clicks the *next*
* button, and should switch to the next of the multiple-pages. It needs
* to be consistent with both isNextEnabled() and isAtEnd().
*
* In particular: when isAtEnd() returns false, next() is called when
* the user clicks the button and a new page should be shown by this
* view step. When isAtEnd() returns true, clicking the button will
* switch to the next view step in sequence, rather than a next page
* in the current view step.
*/
virtual void next();
/// @brief Multi-page support, go back
virtual void back();
/// @brief Can the user click *next* with currently-filled-in data?
virtual bool isNextEnabled() const = 0;
/// @brief Can the user click *previous* with currently-filled-in data?
virtual bool isBackEnabled() const = 0;
/**
* @brief Multi-page support, switch to previous view step?
*
* For a multi-page view step, this indicates that the first (beginning)
* page is showing. Clicking *previous* when at the beginning of a view
* step, switches to the previous step, not the previous page of the
* current view step.
*/
virtual bool isAtBeginning() const = 0;
/// @brief Multi-page support, switch to next view step?
virtual bool isAtEnd() const = 0;
/**
@ -91,6 +118,12 @@ public:
*/
virtual void onLeave();
/**
* @brief Jobs needed to run this viewstep
*
* When a ViewStep is listed in the exec section, its jobs are executed instead.
* This function returns that list of jobs; an empty list is ok.
*/
virtual JobList jobs() const = 0;
void setModuleInstanceKey( const QString& instanceKey );
@ -101,9 +134,18 @@ public:
virtual void setConfigurationMap( const QVariantMap& configurationMap );
/**
* @brief Can this module proceed, on this machine?
*
* This is called asynchronously at startup, and returns a list of
* the requirements that the module has checked, and their status.
* See Calamares::RequirementEntry for details.
*/
virtual RequirementsList checkRequirements();
signals:
/// @brief Tells the viewmanager to enable the *next* button according to @p status
void nextStatusChanged( bool status );
void done();
/* Emitted when the viewstep thinks it needs more space than is currently
* available for display. @p enlarge is the requested additional space,

View File

@ -67,18 +67,6 @@ FinishedViewStep::widget()
}
void
FinishedViewStep::next()
{
emit done();
}
void
FinishedViewStep::back()
{}
bool
FinishedViewStep::isNextEnabled() const
{

View File

@ -40,9 +40,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -53,18 +53,6 @@ InteractiveTerminalViewStep::widget()
}
void
InteractiveTerminalViewStep::next()
{
emit done();
}
void
InteractiveTerminalViewStep::back()
{}
bool
InteractiveTerminalViewStep::isNextEnabled() const
{

View File

@ -41,9 +41,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -65,19 +65,6 @@ KeyboardViewStep::widget()
}
void
KeyboardViewStep::next()
{
//TODO: actually save those settings somewhere
emit done();
}
void
KeyboardViewStep::back()
{}
bool
KeyboardViewStep::isNextEnabled() const
{

View File

@ -42,9 +42,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -59,18 +59,6 @@ LicenseViewStep::widget()
}
void
LicenseViewStep::next()
{
emit done();
}
void
LicenseViewStep::back()
{}
bool
LicenseViewStep::isNextEnabled() const
{

View File

@ -42,9 +42,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -190,18 +190,6 @@ LocaleViewStep::widget()
}
void
LocaleViewStep::next()
{
emit done();
}
void
LocaleViewStep::back()
{}
bool
LocaleViewStep::isNextEnabled() const
{

View File

@ -45,9 +45,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -69,18 +69,6 @@ NetInstallViewStep::widget()
}
void
NetInstallViewStep::next()
{
emit done();
}
void
NetInstallViewStep::back()
{}
bool
NetInstallViewStep::isNextEnabled() const
{

View File

@ -42,9 +42,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -882,11 +882,6 @@ ChoicePage::updateDeviceStatePreview()
PartitionModel* model = new PartitionModel( m_beforePartitionBarsView );
model->init( deviceBefore, m_core->osproberEntries() );
// The QObject parents tree is meaningful for memory management here,
// see qDeleteAll above.
deviceBefore->setParent( model ); // Can't reparent across threads
model->setParent( m_beforePartitionBarsView );
m_beforePartitionBarsView->setModel( model );
m_beforePartitionLabelsView->setModel( model );

View File

@ -60,6 +60,8 @@
#include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>
#include <unistd.h> // For sleep(3)
PartitionViewStep::PartitionViewStep( QObject* parent )
: Calamares::ViewStep( parent )
, m_core( nullptr )
@ -213,8 +215,8 @@ PartitionViewStep::createSummaryWidget() const
else // multiple disk previews!
{
diskInfoLabel->setText( tr( "Disk <strong>%1</strong> (%2)" )
.arg( info.deviceNode )
.arg( info.deviceName ) );
.arg( info.deviceNode )
.arg( info.deviceName ) );
}
formLayout->addRow( diskInfoLabel );
@ -223,9 +225,9 @@ PartitionViewStep::createSummaryWidget() const
QVBoxLayout* field;
PartitionBarsView::NestedPartitionsMode mode = Calamares::JobQueue::instance()->globalStorage()->
value( "drawNestedPartitions" ).toBool() ?
PartitionBarsView::DrawNestedPartitions :
PartitionBarsView::NoNestedPartitions;
value( "drawNestedPartitions" ).toBool() ?
PartitionBarsView::DrawNestedPartitions :
PartitionBarsView::NoNestedPartitions;
preview = new PartitionBarsView;
preview->setNestedPartitionsMode( mode );
previewLabels = new PartitionLabelsView;
@ -263,7 +265,7 @@ PartitionViewStep::createSummaryWidget() const
foreach ( const Calamares::job_ptr& job, jobs() )
{
if ( !job->prettyDescription().isEmpty() )
jobsLines.append( job->prettyDescription() );
jobsLines.append( job->prettyDescription() );
}
if ( !jobsLines.isEmpty() )
{
@ -292,25 +294,8 @@ PartitionViewStep::next()
if ( m_core->isDirty() )
m_manualPartitionPage->onRevertClicked();
}
else if ( m_choicePage->currentChoice() == ChoicePage::Erase )
{
emit done();
return;
}
else if ( m_choicePage->currentChoice() == ChoicePage::Alongside )
{
emit done();
return;
}
else if ( m_choicePage->currentChoice() == ChoicePage::Replace )
{
emit done();
return;
}
cDebug() << "Choice applied: " << m_choicePage->currentChoice();
return;
}
emit done();
}
@ -360,8 +345,8 @@ PartitionViewStep::isAtEnd() const
if ( m_choicePage == m_widget->currentWidget() )
{
if ( m_choicePage->currentChoice() == ChoicePage::Erase ||
m_choicePage->currentChoice() == ChoicePage::Replace ||
m_choicePage->currentChoice() == ChoicePage::Alongside )
m_choicePage->currentChoice() == ChoicePage::Replace ||
m_choicePage->currentChoice() == ChoicePage::Alongside )
return true;
return false;
}
@ -374,7 +359,7 @@ PartitionViewStep::onActivate()
{
// if we're coming back to PVS from the next VS
if ( m_widget->currentWidget() == m_choicePage &&
m_choicePage->currentChoice() == ChoicePage::Alongside )
m_choicePage->currentChoice() == ChoicePage::Alongside )
{
m_choicePage->applyActionChoice( ChoicePage::Alongside );
// m_choicePage->reset();
@ -397,7 +382,7 @@ PartitionViewStep::onLeave()
if ( PartUtils::isEfiSystem() )
{
QString espMountPoint = Calamares::JobQueue::instance()->globalStorage()->
value( "efiSystemPartition").toString();
value( "efiSystemPartition" ).toString();
Partition* esp = m_core->findPartitionByMountPoint( espMountPoint );
QString message;
@ -452,7 +437,7 @@ PartitionViewStep::onLeave()
// If the root partition is encrypted, and there's a separate boot
// partition which is not encrypted
if ( root_p->fileSystem().type() == FileSystem::Luks &&
boot_p->fileSystem().type() != FileSystem::Luks )
boot_p->fileSystem().type() != FileSystem::Luks )
{
message = tr( "Boot partition not encrypted" );
description = tr( "A separate boot partition was set up together with "
@ -601,17 +586,18 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
// Now that we have the config, we load the PartitionCoreModule in the background
// because it could take a while. Then when it's done, we can set up the widgets
// and remove the spinner.
QFutureWatcher< void >* watcher = new QFutureWatcher< void >();
connect( watcher, &QFutureWatcher< void >::finished,
this, [ this, watcher, choices ]
m_future = new QFutureWatcher< void >();
connect( m_future, &QFutureWatcher< void >::finished,
this, [ this ]
{
continueLoading();
watcher->deleteLater();
this->m_future->deleteLater();
this->m_future = nullptr;
} );
QFuture< void > future =
QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
watcher->setFuture( future );
QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
m_future->setFuture( future );
if ( configurationMap.contains( "partitionLayout" ) )
{
@ -630,5 +616,24 @@ PartitionViewStep::jobs() const
return m_core->jobs();
}
Calamares::RequirementsList
PartitionViewStep::checkRequirements()
{
if ( m_future )
m_future->waitForFinished();
Calamares::RequirementsList l;
l.append(
{
QLatin1Literal( "partitions" ),
[]{ return tr( "has at least one disk device available." ); },
[]{ return tr( "There are no partitons to install on." ); },
m_core->deviceModel()->rowCount() > 0, // satisfied
true // required
} );
return l;
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( PartitionViewStepFactory, registerPlugin<PartitionViewStep>(); )

View File

@ -36,6 +36,8 @@ class PartitionPage;
class PartitionCoreModule;
class QStackedWidget;
template<typename T> class QFutureWatcher;
/**
* The starting point of the module. Instantiates PartitionCoreModule,
* ChoicePage and PartitionPage, then connects them.
@ -69,6 +71,8 @@ public:
QList< Calamares::job_ptr > jobs() const override;
Calamares::RequirementsList checkRequirements() override;
private:
void initPartitionCoreModule();
void continueLoading();
@ -79,6 +83,7 @@ private:
PartitionPage* m_manualPartitionPage;
QWidget* m_waitingWidget;
QFutureWatcher<void>* m_future;
QSet< PartitionActions::Choices::SwapChoice > m_swapChoices;
};

View File

@ -76,18 +76,6 @@ PlasmaLnfViewStep::widget()
}
void
PlasmaLnfViewStep::next()
{
emit done();
}
void
PlasmaLnfViewStep::back()
{}
bool
PlasmaLnfViewStep::isNextEnabled() const
{

View File

@ -41,9 +41,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -51,18 +51,6 @@ SummaryViewStep::widget()
}
void
SummaryViewStep::next()
{
emit done();
}
void
SummaryViewStep::back()
{}
bool
SummaryViewStep::isNextEnabled() const
{

View File

@ -40,9 +40,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -67,18 +67,6 @@ TrackingViewStep::widget()
}
void
TrackingViewStep::next()
{
emit done();
}
void
TrackingViewStep::back()
{}
bool
TrackingViewStep::isNextEnabled() const
{

View File

@ -43,9 +43,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -61,18 +61,6 @@ UsersViewStep::widget()
}
void
UsersViewStep::next()
{
emit done();
}
void
UsersViewStep::back()
{}
bool
UsersViewStep::isNextEnabled() const
{

View File

@ -43,9 +43,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;

View File

@ -72,18 +72,6 @@ WebViewStep::widget()
}
void
WebViewStep::next()
{
emit done();
}
void
WebViewStep::back()
{}
bool
WebViewStep::isNextEnabled() const
{

View File

@ -50,8 +50,6 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
void onActivate() override;
bool isNextEnabled() const override;

View File

@ -15,9 +15,10 @@ endif()
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
set( CHECKER_SOURCES
checker/CheckItemWidget.cpp
checker/CheckerWidget.cpp
checker/RequirementsChecker.cpp
checker/CheckerContainer.cpp
checker/ResultWidget.cpp
checker/ResultsListWidget.cpp
checker/GeneralRequirements.cpp
${PARTMAN_SRC}
)

View File

@ -22,10 +22,12 @@
#include "ui_WelcomePage.h"
#include "CalamaresVersion.h"
#include "checker/RequirementsChecker.h"
#include "checker/CheckerContainer.h"
#include "utils/Logger.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h"
#include "modulesystem/ModuleManager.h"
#include "ViewManager.h"
#include <QApplication>
@ -39,11 +41,14 @@
#include "Branding.h"
WelcomePage::WelcomePage( RequirementsChecker* requirementsChecker, QWidget* parent )
WelcomePage::WelcomePage( QWidget* parent )
: QWidget( parent )
, ui( new Ui::WelcomePage )
, m_requirementsChecker( requirementsChecker )
, m_checkingWidget( new CheckerContainer( this ) )
{
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsResult, m_checkingWidget, &CheckerContainer::requirementsChecked );
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, m_checkingWidget, &CheckerContainer::requirementsComplete );
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsProgress, m_checkingWidget, &CheckerContainer::requirementsProgress );
ui->setupUi( this );
ui->verticalLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() * 2 );
@ -102,7 +107,7 @@ WelcomePage::WelcomePage( RequirementsChecker* requirementsChecker, QWidget* par
mb.exec();
} );
ui->verticalLayout->insertWidget( 3, m_requirementsChecker->widget() );
ui->verticalLayout->insertWidget( 3, m_checkingWidget);
}
@ -277,3 +282,8 @@ WelcomePage::focusInEvent( QFocusEvent* e )
ui->languageWidget->setFocus();
e->accept();
}
bool WelcomePage::verdict() const
{
return m_checkingWidget->verdict();
}

View File

@ -26,26 +26,27 @@ namespace Ui
class WelcomePage;
}
class RequirementsChecker;
class CheckerContainer;
class WelcomePage : public QWidget
{
Q_OBJECT
public:
explicit WelcomePage( RequirementsChecker* requirementsChecker,
QWidget* parent = nullptr );
explicit WelcomePage( QWidget* parent = nullptr );
void setUpLinks( bool showSupportUrl,
bool showKnownIssuesUrl,
bool showReleaseNotesUrl );
bool verdict() const;
protected:
void focusInEvent( QFocusEvent* e ) override; //choose the child widget to focus
private:
void initLanguages();
Ui::WelcomePage* ui;
RequirementsChecker* m_requirementsChecker;
CheckerContainer* m_checkingWidget;
};
#endif // WELCOMEPAGE_H

View File

@ -20,9 +20,10 @@
#include "WelcomeViewStep.h"
#include "WelcomePage.h"
#include "checker/RequirementsChecker.h"
#include "utils/Logger.h"
#include "checker/GeneralRequirements.h"
#include "modulesystem/ModuleManager.h"
#include "utils/Logger.h"
#include <QVariant>
@ -30,12 +31,10 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeViewStepFactory, registerPlugin<Welc
WelcomeViewStep::WelcomeViewStep( QObject* parent )
: Calamares::ViewStep( parent )
, m_requirementsChecker( new RequirementsChecker( this ) )
, m_requirementsChecker( new GeneralRequirements( this ) )
{
emit nextStatusChanged( true );
m_widget = new WelcomePage( m_requirementsChecker );
connect( m_requirementsChecker, &RequirementsChecker::verdictChanged,
this, &WelcomeViewStep::nextStatusChanged );
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, this, &WelcomeViewStep::nextStatusChanged );
m_widget = new WelcomePage();
}
@ -60,22 +59,10 @@ WelcomeViewStep::widget()
}
void
WelcomeViewStep::next()
{
emit done();
}
void
WelcomeViewStep::back()
{}
bool
WelcomeViewStep::isNextEnabled() const
{
return m_requirementsChecker->verdict();
return m_widget->verdict();
}
@ -100,10 +87,10 @@ WelcomeViewStep::isAtEnd() const
}
QList< Calamares::job_ptr >
Calamares::JobList
WelcomeViewStep::jobs() const
{
return QList< Calamares::job_ptr >();
return Calamares::JobList();
}
@ -135,3 +122,7 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap )
"module configuration.";
}
Calamares::RequirementsList WelcomeViewStep::checkRequirements()
{
return m_requirementsChecker->checkRequirements();
}

View File

@ -21,6 +21,7 @@
#include <QObject>
#include <modulesystem/Requirement.h>
#include <utils/PluginFactory.h>
#include <viewpages/ViewStep.h>
@ -29,7 +30,7 @@
#include <QVariantMap>
class WelcomePage;
class RequirementsChecker;
class GeneralRequirements;
class PLUGINDLLEXPORT WelcomeViewStep : public Calamares::ViewStep
{
@ -43,23 +44,21 @@ public:
QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
QList< Calamares::job_ptr > jobs() const override;
Calamares::JobList jobs() const override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
Calamares::RequirementsList checkRequirements() override;
private:
WelcomePage* m_widget;
RequirementsChecker* m_requirementsChecker;
GeneralRequirements* m_requirementsChecker;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeViewStepFactory )

View File

@ -0,0 +1,83 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
* Copyright 2017, Gabriel Craciunescu <crazy@frugalware.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
/* Based on code extracted from RequirementsChecker.cpp */
#include "CheckerContainer.h"
#include "ResultsListWidget.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include "widgets/WaitingWidget.h"
CheckerContainer::CheckerContainer( QWidget* parent )
: QWidget( parent )
, m_waitingWidget( new WaitingWidget( QString(), this ) )
, m_checkerWidget( nullptr )
, m_verdict( false )
{
QBoxLayout* mainLayout = new QHBoxLayout;
setLayout( mainLayout );
CalamaresUtils::unmarginLayout( mainLayout );
mainLayout->addWidget( m_waitingWidget );
CALAMARES_RETRANSLATE(
if ( m_waitingWidget )
m_waitingWidget->setText( tr( "Gathering system information..." ) );
)
}
CheckerContainer::~CheckerContainer()
{
delete m_waitingWidget;
delete m_checkerWidget;
}
void CheckerContainer::requirementsComplete( bool ok )
{
layout()->removeWidget( m_waitingWidget );
m_waitingWidget->deleteLater();
m_waitingWidget = nullptr; // Don't delete in destructor
m_checkerWidget = new ResultsListWidget( this );
m_checkerWidget->init( m_requirements );
layout()->addWidget( m_checkerWidget );
m_verdict = ok;
}
void CheckerContainer::requirementsChecked(const Calamares::RequirementsList& l)
{
m_requirements.append( l );
}
void CheckerContainer::requirementsProgress(const QString& message)
{
if ( m_waitingWidget )
m_waitingWidget->setText( message );
}
bool CheckerContainer::verdict() const
{
return m_verdict;
}

View File

@ -0,0 +1,64 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017, Gabriel Craciunescu <crazy@frugalware.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
/* Based on code extracted from RequirementsChecker.cpp */
#ifndef CHECKERCONTAINER_H
#define CHECKERCONTAINER_H
#include <QWidget>
#include "modulesystem/Requirement.h"
class ResultsListWidget;
class WaitingWidget;
/**
* A widget that collects requirements results; until the results are
* all in, displays a spinner / waiting widget. Then it switches to
* a (list) diplay of the results, plus some explanation of the
* overall state of the entire list of results.
*/
class CheckerContainer : public QWidget
{
Q_OBJECT
public:
explicit CheckerContainer( QWidget* parent = nullptr );
virtual ~CheckerContainer();
bool verdict() const;
public slots:
void requirementsChecked( const Calamares::RequirementsList& );
/** @brief All the requirements are complete, switch to list view */
void requirementsComplete( bool );
void requirementsProgress( const QString& message );
protected:
WaitingWidget *m_waitingWidget;
ResultsListWidget *m_checkerWidget;
Calamares::RequirementsList m_requirements;
bool m_verdict;
} ;
#endif

View File

@ -18,11 +18,12 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "RequirementsChecker.h"
#include "GeneralRequirements.h"
#include "CheckerWidget.h"
#include "CheckerContainer.h"
#include "partman_devices.h"
#include "modulesystem/Requirement.h"
#include "widgets/WaitingWidget.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
@ -30,6 +31,7 @@
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Units.h"
#include "JobQueue.h"
#include "GlobalStorage.h"
@ -51,166 +53,113 @@
#include <unistd.h> //geteuid
RequirementsChecker::RequirementsChecker( QObject* parent )
GeneralRequirements::GeneralRequirements( QObject* parent )
: QObject( parent )
, m_widget( new QWidget() )
, m_requiredStorageGB( -1 )
, m_requiredRamGB( -1 )
, m_actualWidget( new CheckerWidget() )
, m_verdict( false )
{
QBoxLayout* mainLayout = new QHBoxLayout;
m_widget->setLayout( mainLayout );
CalamaresUtils::unmarginLayout( mainLayout );
}
WaitingWidget* waitingWidget = new WaitingWidget( QString() );
mainLayout->addWidget( waitingWidget );
CALAMARES_RETRANSLATE( waitingWidget->setText( tr( "Gathering system information..." ) ); )
Calamares::RequirementsList GeneralRequirements::checkRequirements()
{
QSize availableSize = qApp->desktop()->availableGeometry().size();
QSize availableSize = qApp->desktop()->availableGeometry( m_widget ).size();
bool enoughStorage = false;
bool enoughRam = false;
bool hasPower = false;
bool hasInternet = false;
bool isRoot = false;
bool enoughScreen = (availableSize.width() >= CalamaresUtils::windowMinimumWidth) && (availableSize.height() >= CalamaresUtils::windowMinimumHeight);
QTimer* timer = new QTimer;
timer->setSingleShot( true );
connect( timer, &QTimer::timeout,
[=]()
qint64 requiredStorageB = CalamaresUtils::GiBtoBytes(m_requiredStorageGB);
cDebug() << "Need at least storage bytes:" << requiredStorageB;
if ( m_entriesToCheck.contains( "storage" ) )
enoughStorage = checkEnoughStorage( requiredStorageB );
qint64 requiredRamB = CalamaresUtils::GiBtoBytes(m_requiredRamGB);
cDebug() << "Need at least ram bytes:" << requiredRamB;
if ( m_entriesToCheck.contains( "ram" ) )
enoughRam = checkEnoughRam( requiredRamB );
if ( m_entriesToCheck.contains( "power" ) )
hasPower = checkHasPower();
if ( m_entriesToCheck.contains( "internet" ) )
hasInternet = checkHasInternet();
if ( m_entriesToCheck.contains( "root" ) )
isRoot = checkIsRoot();
using TR = Logger::DebugRow<const char *, bool>;
cDebug() << "GeneralRequirements output:"
<< TR("enoughStorage", enoughStorage)
<< TR("enoughRam", enoughRam)
<< TR("hasPower", hasPower)
<< TR("hasInternet", hasInternet)
<< TR("isRoot", isRoot);
Calamares::RequirementsList checkEntries;
foreach ( const QString& entry, m_entriesToCheck )
{
bool enoughStorage = false;
bool enoughRam = false;
bool hasPower = false;
bool hasInternet = false;
bool isRoot = false;
bool enoughScreen = (availableSize.width() >= CalamaresUtils::windowMinimumWidth) && (availableSize.height() >= CalamaresUtils::windowMinimumHeight);
qint64 requiredStorageB = CalamaresUtils::GiBtoBytes(m_requiredStorageGB);
cDebug() << "Need at least storage bytes:" << requiredStorageB;
if ( m_entriesToCheck.contains( "storage" ) )
enoughStorage = checkEnoughStorage( requiredStorageB );
qint64 requiredRamB = CalamaresUtils::GiBtoBytes(m_requiredRamGB);
cDebug() << "Need at least ram bytes:" << requiredRamB;
if ( m_entriesToCheck.contains( "ram" ) )
enoughRam = checkEnoughRam( requiredRamB );
if ( m_entriesToCheck.contains( "power" ) )
hasPower = checkHasPower();
if ( m_entriesToCheck.contains( "internet" ) )
hasInternet = checkHasInternet();
if ( m_entriesToCheck.contains( "root" ) )
isRoot = checkIsRoot();
using TR = Logger::DebugRow<const char *, bool>;
cDebug() << "RequirementsChecker output:"
<< TR("enoughStorage", enoughStorage)
<< TR("enoughRam", enoughRam)
<< TR("hasPower", hasPower)
<< TR("hasInternet", hasInternet)
<< TR("isRoot", isRoot);
QList< PrepareEntry > checkEntries;
foreach ( const QString& entry, m_entriesToCheck )
{
if ( entry == "storage" )
checkEntries.append( {
entry,
[this]{ return tr( "has at least %1 GB available drive space" )
.arg( m_requiredStorageGB ); },
[this]{ return tr( "There is not enough drive space. At least %1 GB is required." )
.arg( m_requiredStorageGB ); },
enoughStorage,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "ram" )
checkEntries.append( {
entry,
[this]{ return tr( "has at least %1 GB working memory" )
.arg( m_requiredRamGB ); },
[this]{ return tr( "The system does not have enough working memory. At least %1 GB is required." )
.arg( m_requiredRamGB ); },
enoughRam,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "power" )
checkEntries.append( {
entry,
[this]{ return tr( "is plugged in to a power source" ); },
[this]{ return tr( "The system is not plugged in to a power source." ); },
hasPower,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "internet" )
checkEntries.append( {
entry,
[this]{ return tr( "is connected to the Internet" ); },
[this]{ return tr( "The system is not connected to the Internet." ); },
hasInternet,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "root" )
checkEntries.append( {
entry,
[this]{ return QString(); }, //we hide it
[this]{ return tr( "The installer is not running with administrator rights." ); },
isRoot,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "screen" )
checkEntries.append( {
entry,
[this]{ return QString(); }, // we hide it
[this]{ return tr( "The screen is too small to display the installer." ); },
enoughScreen,
false
} );
}
m_actualWidget->init( checkEntries );
m_widget->layout()->removeWidget( waitingWidget );
waitingWidget->deleteLater();
m_actualWidget->setParent( m_widget );
m_widget->layout()->addWidget( m_actualWidget );
bool canGoNext = true;
foreach ( const PrepareEntry& entry, checkEntries )
{
if ( !entry.checked && entry.required )
{
canGoNext = false;
break;
}
}
m_verdict = canGoNext;
emit verdictChanged( m_verdict );
if ( canGoNext )
detectFirmwareType();
timer->deleteLater();
} );
timer->start( 0 );
emit verdictChanged( true );
}
RequirementsChecker::~RequirementsChecker()
{
if ( m_widget && m_widget->parent() == nullptr )
m_widget->deleteLater();
}
QWidget*
RequirementsChecker::widget() const
{
return m_widget;
if ( entry == "storage" )
checkEntries.append( {
entry,
[this]{ return tr( "has at least %1 GB available drive space" )
.arg( m_requiredStorageGB ); },
[this]{ return tr( "There is not enough drive space. At least %1 GB is required." )
.arg( m_requiredStorageGB ); },
enoughStorage,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "ram" )
checkEntries.append( {
entry,
[this]{ return tr( "has at least %1 GB working memory" )
.arg( m_requiredRamGB ); },
[this]{ return tr( "The system does not have enough working memory. At least %1 GB is required." )
.arg( m_requiredRamGB ); },
enoughRam,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "power" )
checkEntries.append( {
entry,
[this]{ return tr( "is plugged in to a power source" ); },
[this]{ return tr( "The system is not plugged in to a power source." ); },
hasPower,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "internet" )
checkEntries.append( {
entry,
[this]{ return tr( "is connected to the Internet" ); },
[this]{ return tr( "The system is not connected to the Internet." ); },
hasInternet,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "root" )
checkEntries.append( {
entry,
[this]{ return QString(); }, //we hide it
[this]{ return tr( "The installer is not running with administrator rights." ); },
isRoot,
m_entriesToRequire.contains( entry )
} );
else if ( entry == "screen" )
checkEntries.append( {
entry,
[this]{ return QString(); }, // we hide it
[this]{ return tr( "The screen is too small to display the installer." ); },
enoughScreen,
false
} );
}
return checkEntries;
}
void
RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
GeneralRequirements::setConfigurationMap( const QVariantMap& configurationMap )
{
bool incompleteConfiguration = false;
@ -222,7 +171,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
}
else
{
cWarning() << "RequirementsChecker entry 'check' is incomplete.";
cWarning() << "GeneralRequirements entry 'check' is incomplete.";
incompleteConfiguration = true;
}
@ -234,14 +183,25 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
}
else
{
cWarning() << "RequirementsChecker entry 'required' is incomplete.";
cWarning() << "GeneralRequirements entry 'required' is incomplete.";
incompleteConfiguration = true;
}
#ifdef WITHOUT_LIBPARTED
if ( m_entriesToCheck.contains( "storage" ) || m_entriesToRequire.contains( "storage" ) )
{
// Warn, but also drop the required bit because otherwise installation
// will be impossible (because the check always returns false).
cWarning() << "GeneralRequirements checks 'storage' but libparted is disabled.";
m_entriesToCheck.removeAll( "storage" );
m_entriesToRequire.removeAll( "storage" );
}
#endif
// Help out with consistency, but don't fix
for ( const auto& r : m_entriesToRequire )
if ( !m_entriesToCheck.contains( r ) )
cWarning() << "RequirementsChecker requires" << r << "but does not check it.";
cWarning() << "GeneralRequirements requires" << r << "but does not check it.";
if ( configurationMap.contains( "requiredStorage" ) &&
( configurationMap.value( "requiredStorage" ).type() == QVariant::Double ||
@ -251,7 +211,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
m_requiredStorageGB = configurationMap.value( "requiredStorage" ).toDouble( &ok );
if ( !ok )
{
cWarning() << "RequirementsChecker entry 'requiredStorage' is invalid.";
cWarning() << "GeneralRequirements entry 'requiredStorage' is invalid.";
m_requiredStorageGB = 3.;
}
@ -259,7 +219,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
}
else
{
cWarning() << "RequirementsChecker entry 'requiredStorage' is missing.";
cWarning() << "GeneralRequirements entry 'requiredStorage' is missing.";
m_requiredStorageGB = 3.;
incompleteConfiguration = true;
}
@ -272,14 +232,14 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
m_requiredRamGB = configurationMap.value( "requiredRam" ).toDouble( &ok );
if ( !ok )
{
cWarning() << "RequirementsChecker entry 'requiredRam' is invalid.";
cWarning() << "GeneralRequirements entry 'requiredRam' is invalid.";
m_requiredRamGB = 1.;
incompleteConfiguration = true;
}
}
else
{
cWarning() << "RequirementsChecker entry 'requiredRam' is missing.";
cWarning() << "GeneralRequirements entry 'requiredRam' is missing.";
m_requiredRamGB = 1.;
incompleteConfiguration = true;
}
@ -291,7 +251,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
if ( m_checkHasInternetUrl.isEmpty() ||
!QUrl( m_checkHasInternetUrl ).isValid() )
{
cWarning() << "RequirementsChecker entry 'internetCheckUrl' is invalid in welcome.conf" << m_checkHasInternetUrl
cWarning() << "GeneralRequirements entry 'internetCheckUrl' is invalid in welcome.conf" << m_checkHasInternetUrl
<< "reverting to default (http://example.com).";
m_checkHasInternetUrl = "http://example.com";
incompleteConfiguration = true;
@ -299,7 +259,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
}
else
{
cWarning() << "RequirementsChecker entry 'internetCheckUrl' is undefined in welcome.conf,"
cWarning() << "GeneralRequirements entry 'internetCheckUrl' is undefined in welcome.conf,"
"reverting to default (http://example.com).";
m_checkHasInternetUrl = "http://example.com";
@ -308,24 +268,17 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
if ( incompleteConfiguration )
{
cWarning() << "RequirementsChecker configuration map:" << Logger::DebugMap( configurationMap );
cWarning() << "GeneralRequirements configuration map:" << Logger::DebugMap( configurationMap );
}
}
bool
RequirementsChecker::verdict() const
{
return m_verdict;
}
bool
RequirementsChecker::checkEnoughStorage( qint64 requiredSpace )
GeneralRequirements::checkEnoughStorage( qint64 requiredSpace )
{
#ifdef WITHOUT_LIBPARTED
Q_UNUSED( requiredSpace );
cWarning() << "RequirementsChecker is configured without libparted.";
cWarning() << "GeneralRequirements is configured without libparted.";
return false;
#else
return check_big_enough( requiredSpace );
@ -334,7 +287,7 @@ RequirementsChecker::checkEnoughStorage( qint64 requiredSpace )
bool
RequirementsChecker::checkEnoughRam( qint64 requiredRam )
GeneralRequirements::checkEnoughRam( qint64 requiredRam )
{
// Ignore the guesstimate-factor; we get an under-estimate
// which is probably the usable RAM for programs.
@ -344,7 +297,7 @@ RequirementsChecker::checkEnoughRam( qint64 requiredRam )
bool
RequirementsChecker::checkBatteryExists()
GeneralRequirements::checkBatteryExists()
{
const QFileInfo basePath( "/sys/class/power_supply" );
@ -370,7 +323,7 @@ RequirementsChecker::checkBatteryExists()
bool
RequirementsChecker::checkHasPower()
GeneralRequirements::checkHasPower()
{
const QString UPOWER_SVC_NAME( "org.freedesktop.UPower" );
const QString UPOWER_INTF_NAME( "org.freedesktop.UPower" );
@ -401,10 +354,10 @@ RequirementsChecker::checkHasPower()
bool
RequirementsChecker::checkHasInternet()
GeneralRequirements::checkHasInternet()
{
// default to true in the QNetworkAccessManager::UnknownAccessibility case
QNetworkAccessManager qnam( this );
QNetworkAccessManager qnam;
bool hasInternet = qnam.networkAccessible() == QNetworkAccessManager::Accessible;
if ( !hasInternet && qnam.networkAccessible() == QNetworkAccessManager::UnknownAccessibility )
@ -425,14 +378,14 @@ RequirementsChecker::checkHasInternet()
bool
RequirementsChecker::checkIsRoot()
GeneralRequirements::checkIsRoot()
{
return !geteuid();
}
void
RequirementsChecker::detectFirmwareType()
GeneralRequirements::detectFirmwareType()
{
QString fwType = QFile::exists( "/sys/firmware/efi/efivars" ) ? "efi" : "bios";
Calamares::JobQueue::instance()->globalStorage()->insert( "firmwareType", fwType );

View File

@ -17,52 +17,23 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REQUIREMENTSCHECKER_H
#define REQUIREMENTSCHECKER_H
#ifndef GENERALREQUIREMENTS_H
#define GENERALREQUIREMENTS_H
#include <QObject>
#include <QStringList>
#include <functional>
#include "modulesystem/Requirement.h"
class CheckerWidget;
class QWidget;
/**
* An indication of a requirement, which is checked in preparation
* for system installation. An entry has a name and some explanation,
* as well as three meaningful states:
* - checked = true, the requirement is met (green)
* - checked = false, the requirement is not met
* - required = false, warn about it (yellow), no failure
* - required = true, prohibit installation (red)
*/
struct PrepareEntry
{
QString name;
std::function< QString() > enumerationText; //Partial string, inserted in a
//list of requirements to satisfy.
std::function< QString() > negatedText; //Complete sentence about this requirement
//not having been met.
bool checked;
bool required;
};
class RequirementsChecker : public QObject
class GeneralRequirements : public QObject
{
Q_OBJECT
public:
explicit RequirementsChecker( QObject* parent = nullptr );
virtual ~RequirementsChecker();
QWidget* widget() const;
explicit GeneralRequirements( QObject* parent = nullptr );
void setConfigurationMap( const QVariantMap& configurationMap );
bool verdict() const;
signals:
void verdictChanged( bool );
Calamares::RequirementsList checkRequirements();
private:
QStringList m_entriesToCheck;
@ -76,13 +47,9 @@ private:
bool checkIsRoot();
void detectFirmwareType();
QWidget* m_widget;
qreal m_requiredStorageGB;
qreal m_requiredRamGB;
QString m_checkHasInternetUrl;
CheckerWidget* m_actualWidget;
bool m_verdict;
};
#endif // REQUIREMENTSCHECKER_H

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
*
* 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,7 +17,7 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CheckItemWidget.h"
#include "ResultWidget.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
@ -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() ) ) );
}
CheckItemWidget::CheckItemWidget( bool checked,
bool required,
QWidget* parent )
ResultWidget::ResultWidget( bool satisfied,
bool required,
QWidget* parent )
: QWidget( parent )
{
QBoxLayout* mainLayout = new QHBoxLayout;
@ -46,19 +47,17 @@ 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 )
setCondition( m_iconLabel, CalamaresUtils::StatusError );
else
if ( required )
setCondition( m_iconLabel, CalamaresUtils::StatusError );
else
setCondition( m_iconLabel, CalamaresUtils::StatusWarning );
setCondition( m_iconLabel, CalamaresUtils::StatusWarning );
}
void
CheckItemWidget::setText( const QString& text )
ResultWidget::setText( const QString& text )
{
m_textLabel->setText( text );
}

View File

@ -0,0 +1,51 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHECKER_RESULTWIDGET_H
#define CHECKER_RESULTWIDGET_H
#include <QLabel>
/**
* @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:
/**
* @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 // CHECKER_RESULTWIDGET_H

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
*
* 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,9 +17,9 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CheckerWidget.h"
#include "ResultsListWidget.h"
#include "CheckItemWidget.h"
#include "ResultWidget.h"
#include "Branding.h"
#include "utils/CalamaresUtilsGui.h"
@ -33,7 +33,7 @@
#include <QLabel>
CheckerWidget::CheckerWidget( QWidget* parent )
ResultsListWidget::ResultsListWidget( QWidget* parent )
: QWidget( parent )
{
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
@ -53,25 +53,23 @@ CheckerWidget::CheckerWidget( QWidget* parent )
void
CheckerWidget::init( const QList< PrepareEntry >& checkEntries )
ResultsListWidget::init( const Calamares::RequirementsList& checkEntries )
{
bool allChecked = true;
bool requirementsSatisfied = true;
for ( const PrepareEntry& entry : checkEntries )
for ( const auto& entry : checkEntries )
{
if ( !entry.checked )
if ( !entry.satisfied )
{
CheckItemWidget* ciw = new CheckItemWidget( entry.checked, entry.required );
ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory );
CALAMARES_RETRANSLATE( ciw->setText( entry.negatedText() ); )
m_entriesLayout->addWidget( ciw );
ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
allChecked = false;
if ( entry.required )
{
if ( entry.mandatory )
requirementsSatisfied = false;
}
ciw->setAutoFillBackground( true );
QPalette pal( ciw->palette() );
pal.setColor( QPalette::Background, Qt::white );
@ -97,7 +95,7 @@ CheckerWidget::init( const QList< PrepareEntry >& checkEntries )
"requirements for installing %1.<br/>"
"Installation cannot continue. "
"<a href=\"#details\">Details...</a>" )
.arg( *Calamares::Branding::ShortVersionedName ) );
.arg( *Calamares::Branding::ShortVersionedName ) );
)
textLabel->setOpenExternalLinks( false );
connect( textLabel, &QLabel::linkActivated,
@ -114,7 +112,7 @@ CheckerWidget::init( const QList< PrepareEntry >& checkEntries )
"recommended requirements for installing %1.<br/>"
"Installation can continue, but some features "
"might be disabled." )
.arg( *Calamares::Branding::ShortVersionedName ) );
.arg( *Calamares::Branding::ShortVersionedName ) );
)
}
}
@ -122,16 +120,16 @@ CheckerWidget::init( const QList< PrepareEntry >& 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,14 +153,12 @@ CheckerWidget::init( const QList< PrepareEntry >& checkEntries )
)
}
else
{
m_mainLayout->addStretch();
}
}
void
CheckerWidget::showDetailsDialog( const QList< PrepareEntry >& checkEntries )
ResultsListWidget::showDetailsDialog( const Calamares::RequirementsList& checkEntries )
{
QDialog* detailsDialog = new QDialog( this );
QBoxLayout* mainLayout = new QVBoxLayout;
@ -177,12 +173,12 @@ CheckerWidget::showDetailsDialog( const QList< PrepareEntry >& checkEntries )
CalamaresUtils::unmarginLayout( entriesLayout );
mainLayout->addLayout( entriesLayout );
for ( const PrepareEntry& entry : checkEntries )
for ( const auto& entry : checkEntries )
{
if ( entry.enumerationText().isEmpty() )
if ( !entry.hasDetails() )
continue;
CheckItemWidget* ciw = new CheckItemWidget( entry.checked, entry.required );
ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory );
CALAMARES_RETRANSLATE( ciw->setText( entry.enumerationText() ); )
entriesLayout->addWidget( ciw );
ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
@ -194,8 +190,8 @@ CheckerWidget::showDetailsDialog( const QList< PrepareEntry >& checkEntries )
}
QDialogButtonBox* buttonBox = new QDialogButtonBox( QDialogButtonBox::Close,
Qt::Horizontal,
this );
Qt::Horizontal,
this );
mainLayout->addWidget( buttonBox );
detailsDialog->setModal( true );

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,28 +17,28 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHECKERWIDGET_H
#define CHECKERWIDGET_H
#ifndef CHECKER_RESULTSLISTWIDGET_H
#define CHECKER_RESULTSLISTWIDGET_H
#include "RequirementsChecker.h"
#include "modulesystem/Requirement.h"
#include <QBoxLayout>
#include <QWidget>
class CheckerWidget : public QWidget
class ResultsListWidget : public QWidget
{
Q_OBJECT
public:
explicit CheckerWidget( QWidget* parent = nullptr );
explicit ResultsListWidget( QWidget* parent = nullptr );
void init( const QList< PrepareEntry >& checkEntries );
void init( const Calamares::RequirementsList& checkEntries );
private:
void showDetailsDialog( const QList< PrepareEntry >& checkEntries );
void showDetailsDialog( const Calamares::RequirementsList& checkEntries );
QBoxLayout* m_mainLayout;
QBoxLayout* m_entriesLayout;
int m_paddingSize;
};
#endif // CHECKERWIDGET_H
#endif // CHECKER_RESULTSLISTWIDGET_H

View File

@ -1,18 +1,37 @@
# Configuration for the welcome module. The welcome page
# displays some information from the branding file.
# Which parts it displays can be configured through
# the show* variables.
#
# In addition to displaying the welcome page, this module
# can check requirements for installation.
---
# Display settings for various buttons on the welcome page.
showSupportUrl: true
showKnownIssuesUrl: true
showReleaseNotesUrl: true
# Requirements checking. These are general, generic, things
# that are checked. They may not match with the actual requirements
# imposed by other modules in the system.
requirements:
# Amount of available disk, in GB. Floating-point is allowed here.
# Note that this does not account for *usable* disk, so it is possible
# to pass this requirement, yet have no space to install to.
requiredStorage: 5.5
# Amount of available RAM, in GB. Floating-point is allowed here.
requiredRam: 1.0
# To check for internet connectivity, Calamares does a HTTP GET
# on this URL; on success (e.g. HTTP code 200) internet is OK.
internetCheckUrl: http://google.com
# List conditions to check. Each listed condition will be
# probed in some way, and yields true or false according to
# the host system satisfying the condition.
#
# This sample file lists all the conditions that are know.
# This sample file lists all the conditions that are known.
check:
- storage
- ram
@ -20,7 +39,7 @@ requirements:
- internet
- root
- screen
# List conditions that must be satisfied (from the list
# List conditions that **must** be satisfied (from the list
# of conditions, above) for installation to proceed.
# If any of these conditions are not met, the user cannot
# continue past the welcome page.