Merge branch 'development' into 3.2.x-stable

This commit is contained in:
Philip Müller 2019-03-12 08:59:02 +01:00
commit 693ea79704
89 changed files with 1558 additions and 879 deletions

31
CHANGES
View File

@ -3,6 +3,36 @@ contributors are listed. Note that Calamares does not have a historical
changelog -- this log starts with version 3.2.0. The release notes on the
website will have to do for older versions.
# 3.2.5 (unreleased) #
This release contains contributions from (alphabetically by first name):
- Arnaud Ferraris
- Dan Simmons
- Gabriel Craciunescu
## 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 ##
* *Bootloader* module: a serious bug introduced in 3.2.4 which prevents
succesful boot after installation on EFI machines, has been repaired.
(Thanks to Gabriel)
* *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):
@ -66,6 +96,7 @@ This release contains contributions from (alphabetically by first name):
the installation media to the target stystem. This can be used, for instance,
for block-level-identical installations.
# 3.2.3 (2019-01-09) #
This release contains contributions from (alphabetically by first name):

View File

@ -75,8 +75,8 @@ set( CALAMARES_DESCRIPTION_SUMMARY
set( CALAMARES_VERSION_MAJOR 3 )
set( CALAMARES_VERSION_MINOR 2 )
set( CALAMARES_VERSION_PATCH 4 )
set( CALAMARES_VERSION_RC 0 )
set( CALAMARES_VERSION_PATCH 5 )
set( CALAMARES_VERSION_RC 1 )
### Transifex (languages) info
@ -240,7 +240,7 @@ include( CMakeColors )
### DEPENDENCIES
#
find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Core Gui Widgets LinguistTools Svg Quick QuickWidgets )
find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Concurrent Core Gui Widgets LinguistTools Svg Quick QuickWidgets )
find_package( YAMLCPP ${YAMLCPP_VERSION} REQUIRED )
if( INSTALL_POLKIT )
find_package( PolkitQt5-1 REQUIRED )

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

@ -2,7 +2,7 @@
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017-2018, 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
@ -68,9 +68,9 @@ logLevel()
}
static void
log( const char* msg, unsigned int debugLevel, bool toDisk = true )
log( const char* msg, unsigned int debugLevel )
{
if ( toDisk || debugLevel < s_threshold )
if ( true )
{
QMutexLocker lock( &s_mutex );

View File

@ -51,7 +51,7 @@ struct NamedEnumTable
*
* static const NamedEnumTable<Colors> c{ {"red", Colors::Red } };
*/
NamedEnumTable( const std::initializer_list< pair_t >& v ) : table( v ) { /* static_assert( v.size() > 0 ); */ };
NamedEnumTable( const std::initializer_list< pair_t >& v ) : table( v ) { /* static_assert( v.size() > 0 ); */ }
/** @brief Find a name @p s in the table.
*

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,10 +25,16 @@
namespace CalamaresUtils
{
/** User defined literals, 1_KiB is 1 KibiByte (= 2^10 bytes) */
constexpr qint64 operator ""_KiB( unsigned long long m )
{
return qint64(m) * 1024;
}
/** User defined literals, 1_MiB is 1 MibiByte (= 2^20 bytes) */
constexpr qint64 operator ""_MiB( unsigned long long m )
{
return qint64(m) * 1024 * 1024;
return operator ""_KiB(m) * 1024;
}
/** User defined literals, 1_GiB is 1 GibiByte (= 2^30 bytes) */
@ -36,6 +43,11 @@ constexpr qint64 operator ""_GiB( unsigned long long m )
return operator ""_MiB(m) * 1024;
}
constexpr qint64 KiBtoBytes( unsigned long long m )
{
return operator ""_KiB( m );
}
constexpr qint64 MiBtoBytes( unsigned long long m )
{
return operator ""_MiB( m );
@ -46,7 +58,12 @@ constexpr qint64 GiBtoBytes( unsigned long long m )
return operator ""_GiB( m );
}
constexpr qint64 MiBToBytes( double m )
constexpr qint64 KiBtoBytes( double m )
{
return qint64(m * 1024);
}
constexpr qint64 MiBtoBytes( double m )
{
return qint64(m * 1024 * 1024);
}

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

@ -8,27 +8,28 @@ Each Calamares module lives in its own directory.
All modules are installed in `$DESTDIR/lib/calamares/modules`.
# Module types
There are two types of Calamares module:
There are two **types** of Calamares module:
* viewmodule, for user-visible modules. These may be in C++, or PythonQt.
* jobmodule, for not-user-visible modules. These may be done in C++,
Python, or as external processes.
# Module interfaces
A viewmodule exposes a UI to the user. The PythonQt-based modules
are considered experimental (and as of march 2019 may be on the
way out again as never-used-much and PythonQt is not packaged
on Debian anymore).
There are three (four) interfaces for Calamares modules:
* qtplugin,
There are three (four) **interfaces** for Calamares modules:
* qtplugin (viewmodules, jobmodules),
* python (jobmodules only),
* pythonqt (optional),
* pythonqt (viewmodules, jobmodules, optional),
* process (jobmodules only).
# Module directory
## Module directory
Each Calamares module lives in its own directory. The contents
of the directory depend on the interface and type of the module.
## Module descriptor
### Module descriptor
A Calamares module must have a *module descriptor file*, named
`module.desc`. For C++ (qtplugin) modules using CMake as a build-
@ -49,25 +50,66 @@ Module descriptors **must** have the following keys:
- *interface* (see below for the different interfaces; generally we
refer to the kinds of modules by their interface)
Module descriptors for Python and PythonQt modules **must** have the following key:
- *script* (the name of the Python script to load, nearly always `main.py`)
Module descriptors **may** have the following keys:
- *required* **unimplemented** (a list of modules which are required for this module
- *requiredModules* (a list of modules which are required for this module
to operate properly)
- *emergency* (a boolean value, set to true to mark the module
as an emergency module)
## Module-specific configuration
### Required Modules
A module may list zero (if it has no requirements) or more modules
by name. As modules are loaded from the global sequence in `settings.conf`,
each module is checked that all of the modules it requires are
already loaded before it. This ensures that if a module needs
another one to fill in globalstorage keys, that happens before
it needs those keys.
### Emergency Modules
Only C++ modules and job modules may be emergency modules. If, during an
*exec* step in the sequence, a module fails, installation as a whole fails
and the install is aborted. If there are emergency modules in the **same**
exec block, those will be executed before the installation is aborted.
Non-emergency modules are not executed.
If an emergency-module fails while processing emergency-modules for
another failed module, that failure is ignored and emergency-module
processing continues.
Use the EMERGENCY keyword in the CMake description of a C++ module
to generate a suitable `module.desc`.
A module that is marked as an emergency module in its module.desc
must **also** set the *emergency* key to *true* in its configuration file
(see below). If it does not, the module is not considered to be an emergency
module after all (this is so that you can have modules that have several
instances, only some of which are actually needed for emergencies).
### Module-specific configuration
A Calamares module **may** read a module configuration file,
named `<modulename>.conf`. If such a file is present in the
module's directory, it is shipped as a *default* configuration file.
module's directory, it can be shipped as a *default* configuration file.
This only happens if the CMake-time option `INSTALL_CONFIG` is on.
The sample configuration files may work and may be suitable for
your distribution, but no guarantee is given about their stability
beyond syntactic correctness.
The module configuration file, if it exists, is a YAML 1.2 document
which contains a YAML map of anything.
All default module configuration files are installed in
All sample module configuration files are installed in
`$DESTDIR/share/calamares/modules` but can be overridden by
files with the same name placed manually (or by the packager)
in `/etc/calamares/modules`.
## C++ modules
Currently the recommended way to write a module which exposes one or more
@ -79,6 +121,8 @@ To add a Qt plugin module, put it in a subdirectory and make sure it has
a `CMakeLists.txt` with a `calamares_add_plugin` call. It will be picked
up automatically by our CMake magic. The `module.desc` file is optional.
## Python modules
Modules may use one of the python interfaces, which may be present
@ -90,7 +134,7 @@ or the experimental pythonqt job- and viewmodule interfaces.
To add a Python or process jobmodule, put it in a subdirectory and make sure
it has a `module.desc`. It will be picked up automatically by our CMake magic.
For all kinds of Python jobs, the key *script* must be set to the name of
the main python file for the job. This is almost universally "main.py".
the main python file for the job. This is almost universally `main.py`.
`CMakeLists.txt` is *not* used for Python and process jobmodules.
@ -113,6 +157,17 @@ function `run()` as entry point. The function `run()` must return `None` if
everything went well, or a tuple `(str,str)` with an error message and
description if something went wrong.
### Python API
**TODO:** this needs documentation
## PythonQt modules
The PythonQt modules are considered experimental and may be removed again
due to low uptake. Their documentation is also almost completely lacking.
### PythonQt Jobmodule
A PythonQt jobmodule implements the experimental Job interface by defining
@ -123,31 +178,18 @@ a subclass of something.
A PythonQt viewmodule implements the experimental View interface by defining
a subclass of something.
### Python API
**TODO:** this needs documentation
## Process jobmodules
A process jobmodule runs a (single) command. The interface is "process",
while the module type must be "job" or "jobmodule".
A process jobmodule runs a (single) command. The interface is *process*,
while the module type must be *job* or *jobmodule*.
The key *command* should have a string as value, which is passed to the
shell -- remember to quote it properly.
## Emergency Modules
Only C++ modules and job modules may be emergency modules. If, during an
*exec* step in the sequence, a module fails, installation as a whole fails
and the install is aborted. If there are emergency modules in the **same**
exec block, those will be executed before the installation is aborted.
Non-emergency modules are not executed.
If an emergency-module fails while processing emergency-modules for
another failed module, that failure is ignored and emergency-module
processing continues.
Use the EMERGENCY keyword in the CMake description of a C++ module
to generate a suitable `module.desc`.
A module that is marked as an emergency module in its module.desc
must **also** set the *emergency* key to *true* in its configuration file.
If it does not, the module is not considered to be an emergency module
after all (this is so that you can have modules that have several
instances, only some of which are actually needed for emergencies.
The module-descriptor key *command* should have a string as value, which is
passed to the shell -- remember to quote it properly. It is generally
recommended to use a *shellprocess* job module instead (less configuration,
easier to have multiple instances).

View File

@ -1,5 +1,8 @@
---
type: "job"
name: "bootloader"
interface: "python"
name: "bootloader"
script: "main.py"
# The partition module sets up the EFI firmware type
# global key, which is used to decide how to install.
requiredModules: [ "partition" ]

View File

@ -768,11 +768,11 @@ def run():
displaymanagers.remove(dm)
if not dm_impl:
return (
_("No display managers selected for the displaymanager module."),
_("The list is empty after checking for installed display managers.")
libcalamares.utils.warning(
"No display managers selected for the displaymanager module. "
"The list is empty after checking for installed display managers."
)
return None
# Pick up remaining settings
if "defaultDesktopEnvironment" in libcalamares.job.configuration:

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

@ -80,7 +80,7 @@ ResizeFSJob::RelativeSize::apply( qint64 totalSectors, qint64 sectorSize )
case unit_t::None:
return -1;
case unit_t::Absolute:
return CalamaresUtils::MiBtoBytes( value() ) / sectorSize;
return CalamaresUtils::MiBtoBytes( static_cast<unsigned long long>( value() ) ) / sectorSize;
case unit_t::Percent:
if ( value() == 100 )
return totalSectors; // Common-case, avoid futzing around

View File

@ -62,12 +62,6 @@ def modify_grub_default(partitions, root_mount_point, distributor):
cryptdevice_params = []
# GRUB needs to decrypt the partition that /boot is on, which may be / or /boot
boot_mountpoint = "/"
for partition in partitions:
if partition["mountPoint"] == "/boot":
boot_mountpoint = "/boot"
if have_dracut:
for partition in partitions:
has_luks = "luksMapperName" in partition
@ -78,7 +72,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
swap_outer_uuid = partition["luksUuid"]
swap_outer_mappername = partition["luksMapperName"]
if (partition["mountPoint"] == boot_mountpoint and has_luks):
if (partition["mountPoint"] == "/" and has_luks):
cryptdevice_params = [
"rd.luks.uuid={!s}".format(partition["luksUuid"])
]
@ -88,7 +82,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
if partition["fs"] == "linuxswap" and not has_luks:
swap_uuid = partition["uuid"]
if (partition["mountPoint"] == boot_mountpoint and has_luks):
if (partition["mountPoint"] == "/" and has_luks):
cryptdevice_params = [
"cryptdevice=UUID={!s}:{!s}".format(
partition["luksUuid"], partition["luksMapperName"]

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

@ -31,7 +31,7 @@ public:
explicit LocaleConfiguration();
/// @brief Create a locale with everything set to the given @p localeName
explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ )
: LocaleConfiguration( localeName, localeName ) { };
: LocaleConfiguration( localeName, localeName ) { }
/// @brief Create a locale with language and formats separate
explicit LocaleConfiguration( const QString& localeName, const QString& formatsName );

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

@ -6,6 +6,7 @@
# Copyright 2014, Kevin Kofler <kevin.kofler@chello.at>
# Copyright 2016, Philip Müller <philm@manjaro.org>
# Copyright 2017, Alf Gaida <agaida@siduction.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

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

@ -2,6 +2,7 @@
*
* Copyright 2015-2016, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -42,6 +43,25 @@
namespace PartUtils
{
static QString
convenienceName( const Partition* const candidate )
{
if ( !candidate->mountPoint().isEmpty() )
return candidate->mountPoint();
if ( !candidate->partitionPath().isEmpty() )
return candidate->partitionPath();
if ( !candidate->devicePath().isEmpty() )
return candidate->devicePath();
if ( !candidate->deviceNode().isEmpty() )
return candidate->devicePath();
QString p;
QTextStream s( &p );
s << (void *)candidate;
return p;
}
bool
canBeReplaced( Partition* candidate )
{
@ -63,12 +83,12 @@ canBeReplaced( Partition* candidate )
<< QString( "(%1GB)" ).arg( requiredStorageB / 1024 / 1024 / 1024 );
cDebug() << "Storage capacity B:" << availableStorageB
<< QString( "(%1GB)" ).arg( availableStorageB / 1024 / 1024 / 1024 )
<< "for" << candidate->partitionPath() << " length:" << candidate->length();
<< "for" << convenienceName( candidate ) << " length:" << candidate->length();
if ( ok &&
availableStorageB > requiredStorageB )
{
cDebug() << "Partition" << candidate->partitionPath() << "authorized for replace install.";
cDebug() << "Partition" << convenienceName( candidate ) << "authorized for replace install.";
return true;
}
@ -85,7 +105,7 @@ canBeResized( Partition* candidate )
return false;
}
cDebug() << "Checking if" << candidate->partitionPath() << "can be resized.";
cDebug() << "Checking if" << convenienceName( candidate ) << "can be resized.";
if ( !candidate->fileSystem().supportGrow() ||
!candidate->fileSystem().supportShrink() )
{
@ -117,7 +137,7 @@ canBeResized( Partition* candidate )
if ( table->numPrimaries() >= table->maxPrimaries() )
{
cDebug() << " .. partition table already has"
cDebug() << " .. partition table already has"
<< table->maxPrimaries() << "primary partitions.";
return false;
}
@ -139,13 +159,13 @@ canBeResized( Partition* candidate )
<< QString( "(%1GB)" ).arg( advisedStorageGB );
cDebug() << "Available storage B:" << availableStorageB
<< QString( "(%1GB)" ).arg( availableStorageB / 1024 / 1024 / 1024 )
<< "for" << candidate->partitionPath() << " length:" << candidate->length()
<< "for" << convenienceName( candidate ) << " length:" << candidate->length()
<< " sectorsUsed:" << candidate->sectorsUsed() << " fsType:" << candidate->fileSystem().name();
if ( ok &&
availableStorageB > advisedStorageB )
{
cDebug() << "Partition" << candidate->partitionPath() << "authorized for resize + autopartition install.";
cDebug() << "Partition" << convenienceName( candidate ) << "authorized for resize + autopartition install.";
return true;
}
@ -198,7 +218,7 @@ lookForFstabEntries( const QString& partitionPath )
mountOptions.append( "noload" );
}
cDebug() << "Checking device" << partitionPath
cDebug() << "Checking device" << partitionPath
<< "for fstab (fs=" << r.getOutput() << ')';
FstabEntryList fstabEntries;
@ -209,9 +229,9 @@ lookForFstabEntries( const QString& partitionPath )
if ( !exit ) // if all is well
{
QFile fstabFile( mountsDir.path() + "/etc/fstab" );
cDebug() << " .. reading" << fstabFile.fileName();
if ( fstabFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
const QStringList fstabLines = QString::fromLocal8Bit( fstabFile.readAll() )
@ -381,7 +401,7 @@ isEfiSystem()
bool
isEfiBootable( const Partition* candidate )
{
cDebug() << "Check EFI bootable" << candidate->partitionPath() << candidate->devicePath();
cDebug() << "Check EFI bootable" << convenienceName( candidate ) << candidate->devicePath();
cDebug() << " .. flags" << candidate->activeFlags();
auto flags = PartitionInfo::flags( candidate );
@ -408,6 +428,149 @@ isEfiBootable( const Partition* candidate )
flags.testFlag( PartitionTable::FlagBoot );
}
QString
findFS( QString fsName, FileSystem::Type* fsType )
{
QStringList fsLanguage { QLatin1Literal( "C" ) }; // Required language list to turn off localization
if ( fsName.isEmpty() )
fsName = QStringLiteral( "ext4" );
FileSystem::Type tmpType = FileSystem::typeForName( fsName, fsLanguage );
if ( tmpType != FileSystem::Unknown )
{
cDebug() << "Found filesystem" << fsName;
if ( fsType )
*fsType = tmpType;
return fsName;
}
// Second pass: try case-insensitive
const auto fstypes = FileSystem::types();
for ( FileSystem::Type t : fstypes )
{
if ( 0 == QString::compare( fsName, FileSystem::nameForType( t, fsLanguage ), Qt::CaseInsensitive ) )
{
QString fsRealName = FileSystem::nameForType( t, fsLanguage );
cDebug() << "Filesystem name" << fsName << "translated to" << fsRealName;
if ( fsType )
*fsType = t;
return fsRealName;
}
}
cDebug() << "Filesystem" << fsName << "not found, using ext4";
fsName = QStringLiteral( "ext4" );
// fsType can be used to check whether fsName was a valid filesystem.
if (fsType)
*fsType = FileSystem::Unknown;
#ifdef DEBUG_FILESYSTEMS
// This bit is for distro's debugging their settings, and shows
// all the strings that KPMCore is matching against for FS type.
{
Logger::CDebug d;
using TR = Logger::DebugRow< int, QString >;
const auto fstypes = FileSystem::types();
d << "Available types (" << fstypes.count() << ')';
for ( FileSystem::Type t : fstypes )
d << TR( static_cast<int>( t ), FileSystem::nameForType( t, fsLanguage ) );
}
#endif
return fsName;
}
static qint64
sizeToBytes( double size, SizeUnit unit, qint64 totalSize )
{
qint64 bytes;
switch ( unit )
{
case SizeUnit::Percent:
bytes = qint64( static_cast<double>( totalSize ) * size / 100.0L );
break;
case SizeUnit::KiB:
bytes = CalamaresUtils::KiBtoBytes(size);
break;
case SizeUnit::MiB:
bytes = CalamaresUtils::MiBtoBytes(size);
break;
case SizeUnit::GiB:
bytes = CalamaresUtils::GiBtoBytes(size);
break;
default:
bytes = size;
break;
}
return bytes;
}
double
parseSizeString( const QString& sizeString, SizeUnit* unit )
{
double value;
bool ok;
QString valueString;
QString unitString;
QRegExp rx( "[KkMmGg%]" );
int pos = rx.indexIn( sizeString );
if (pos > 0)
{
valueString = sizeString.mid( 0, pos );
unitString = sizeString.mid( pos );
}
else
valueString = sizeString;
value = valueString.toDouble( &ok );
if ( !ok )
{
/*
* In case the conversion fails, a size of 100% allows a few cases to pass
* anyway (e.g. when it is the last partition of the layout)
*/
*unit = SizeUnit::Percent;
return 100.0L;
}
if ( unitString.length() > 0 )
{
if ( unitString.at(0) == '%' )
*unit = SizeUnit::Percent;
else if ( unitString.at(0).toUpper() == 'K' )
*unit = SizeUnit::KiB;
else if ( unitString.at(0).toUpper() == 'M' )
*unit = SizeUnit::MiB;
else if ( unitString.at(0).toUpper() == 'G' )
*unit = SizeUnit::GiB;
else
*unit = SizeUnit::Byte;
}
else
{
*unit = SizeUnit::Byte;
}
return value;
}
qint64
parseSizeString( const QString& sizeString, qint64 totalSize )
{
SizeUnit unit;
double value = parseSizeString( sizeString, &unit );
return sizeToBytes( value, unit, totalSize );
}
qint64
sizeToSectors( double size, SizeUnit unit, qint64 totalSectors, qint64 logicalSize )
{
qint64 bytes = sizeToBytes( size, unit, totalSectors * logicalSize );
return bytesToSectors( static_cast<unsigned long long>( bytes ), logicalSize );
}
} // nmamespace PartUtils
/* Implementation of methods for FstabEntry, from OsproberEntry.h */

View File

@ -2,6 +2,7 @@
*
* Copyright 2015-2016, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,7 +22,12 @@
#define PARTUTILS_H
#include "OsproberEntry.h"
#include "utils/Units.h"
// KPMcore
#include <kpmcore/fs/filesystem.h>
// Qt
#include <QString>
class PartitionCoreModule;
@ -29,6 +35,16 @@ class Partition;
namespace PartUtils
{
using CalamaresUtils::MiBtoBytes;
enum SizeUnit
{
Percent = 0,
Byte,
KiB,
MiB,
GiB
};
/**
* @brief canBeReplaced checks whether the given Partition satisfies the criteria
@ -73,6 +89,56 @@ bool isEfiSystem();
* the partition table layout, this may mean different flags.
*/
bool isEfiBootable( const Partition* candidate );
/** @brief translate @p fsName into a recognized name and type
*
* Makes several attempts to translate the string into a
* name that KPMCore will recognize.
* The corresponding filesystem type is stored in @p fsType, and
* its value is FileSystem::Unknown if @p fsName is not recognized.
*/
QString findFS( QString fsName, FileSystem::Type* fsType );
/**
* @brief Parse a partition size string and return its value and unit used.
* @param sizeString the string to parse.
* @param unit pointer to a SizeUnit variable for storing the parsed unit.
* @return the size value, as parsed from the input string.
*/
double parseSizeString( const QString& sizeString, SizeUnit* unit );
/**
* @brief Parse a partition size string and return its value in bytes.
* @param sizeString the string to parse.
* @param totalSize the size of the selected drive (used when the size is expressed in %)
* @return the size value in bytes.
*/
qint64 parseSizeString( const QString& sizeString, qint64 totalSize );
/**
* @brief Convert a partition size to a sectors count.
* @param size the partition size.
* @param unit the partition size unit.
* @param totalSectors the total number of sectors of the selected drive.
* @param logicalSize the sector size, in bytes.
* @return the number of sectors to be used for the given partition size.
*/
qint64 sizeToSectors( double size, SizeUnit unit, qint64 totalSectors, qint64 logicalSize );
constexpr qint64 alignBytesToBlockSize( qint64 bytes, qint64 blocksize )
{
qint64 blocks = bytes / blocksize;
if ( blocks * blocksize != bytes )
++blocks;
return blocks * blocksize;
}
constexpr qint64 bytesToSectors( qint64 bytes, qint64 blocksize )
{
return alignBytesToBlockSize( alignBytesToBlockSize( bytes, blocksize), MiBtoBytes(1ULL) ) / blocksize;
}
}
#endif // PARTUTILS_H

View File

@ -2,6 +2,7 @@
*
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -28,6 +29,7 @@
#include "utils/Units.h"
#include "utils/NamedEnum.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/Logger.h"
@ -38,8 +40,6 @@
namespace PartitionActions
{
using CalamaresUtils::GiBtoBytes;
using CalamaresUtils::MiBtoBytes;
using CalamaresUtils::operator""_GiB;
using CalamaresUtils::operator""_MiB;
@ -82,25 +82,10 @@ swapSuggestion( const qint64 availableSpaceB, Choices::SwapChoice swap )
return suggestedSwapSizeB;
}
constexpr qint64
alignBytesToBlockSize( qint64 bytes, qint64 blocksize )
{
qint64 blocks = bytes / blocksize;
if ( blocks * blocksize != bytes )
++blocks;
return blocks * blocksize;
}
qint64
bytesToSectors( qint64 bytes, qint64 blocksize )
{
return alignBytesToBlockSize( alignBytesToBlockSize( bytes, blocksize), MiBtoBytes(1) ) / blocksize;
}
void
doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionOptions o )
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
QString defaultFsType = o.defaultFsType;
if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown )
defaultFsType = "ext4";
@ -109,19 +94,27 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
// Partition sizes are expressed in MiB, should be multiples of
// the logical sector size (usually 512B). EFI starts with 2MiB
// empty and a 300MiB EFI boot partition, while BIOS starts at
// empty and a EFI boot partition, while BIOS starts at
// the 1MiB boundary (usually sector 2048).
int uefisys_part_sizeB = isEfi ? 300_MiB : 0_MiB;
int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB;
int uefisys_part_sizeB = 0_MiB;
if ( isEfi )
{
if ( gs->contains( "efiSystemPartitionSize" ) )
uefisys_part_sizeB = PartUtils::parseSizeString( gs->value( "efiSystemPartitionSize" ).toString(), dev->capacity() );
else
uefisys_part_sizeB = 300_MiB;
}
// Since sectors count from 0, if the space is 2048 sectors in size,
// the first free sector has number 2048 (and there are 2048 sectors
// before that one, numbered 0..2047).
qint64 firstFreeSector = bytesToSectors( empty_space_sizeB, dev->logicalSize() );
qint64 firstFreeSector = PartUtils::bytesToSectors( empty_space_sizeB, dev->logicalSize() );
if ( isEfi )
{
qint64 efiSectorCount = bytesToSectors( uefisys_part_sizeB, dev->logicalSize() );
qint64 efiSectorCount = PartUtils::bytesToSectors( uefisys_part_sizeB, dev->logicalSize() );
Q_ASSERT( efiSectorCount > 0 );
// Since sectors count from 0, and this partition is created starting

View File

@ -75,8 +75,6 @@ namespace Choices
} // namespace Choices
qint64 bytesToSectors( qint64 bytes, qint64 blocksize );
/**
* @brief doAutopartition sets up an autopartitioning operation on the given Device.
* @param core a pointer to the PartitionCoreModule instance.

View File

@ -154,6 +154,9 @@ public:
void setPartitionFlags( Device* device, Partition* partition, PartitionTable::Flags flags );
/// @brief Retrieve the path where the bootloader will be installed
QString bootLoaderInstallPath() const { return m_bootLoaderInstallPath; }
/// @brief Set the path where the bootloader will be installed
void setBootLoaderInstallPath( const QString& path );
void initLayout();

View File

@ -18,27 +18,50 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "core/PartitionLayout.h"
#include "core/KPMHelpers.h"
#include "core/PartitionActions.h"
#include "core/PartitionInfo.h"
#include "core/PartUtils.h"
#include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystem.h>
static FileSystem::Type
getDefaultFileSystemType()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
FileSystem::Type defaultFS = FileSystem::Ext4;
if ( gs->contains( "defaultFileSystemType" ) )
{
PartUtils::findFS( gs->value( "defaultFileSystemType" ).toString(), &defaultFS);
if ( defaultFS == FileSystem::Unknown )
defaultFS = FileSystem::Ext4;
}
return defaultFS;
}
PartitionLayout::PartitionLayout()
{
m_defaultFsType = getDefaultFileSystemType();
}
PartitionLayout::PartitionLayout( PartitionLayout::PartitionEntry entry )
{
partLayout.append( entry );
m_defaultFsType = getDefaultFileSystemType();
m_partLayout.append( entry );
}
PartitionLayout::PartitionLayout( const PartitionLayout& layout )
: partLayout( layout.partLayout )
: m_partLayout( layout.m_partLayout )
, m_defaultFsType( layout.m_defaultFsType )
{
}
@ -49,64 +72,14 @@ PartitionLayout::~PartitionLayout()
void
PartitionLayout::addEntry( PartitionLayout::PartitionEntry entry )
{
partLayout.append( entry );
}
static double
parseSizeString( const QString& sizeString, PartitionLayout::SizeUnit* unit )
{
double value;
bool ok;
QString valueString;
QString unitString;
QRegExp rx( "[KkMmGg%]" );
int pos = rx.indexIn( sizeString );
if (pos > 0)
{
valueString = sizeString.mid( 0, pos );
unitString = sizeString.mid( pos );
}
else
valueString = sizeString;
value = valueString.toDouble( &ok );
if ( !ok )
{
/*
* In case the conversion fails, a size of 100% allows a few cases to pass
* anyway (e.g. when it is the last partition of the layout)
*/
*unit = PartitionLayout::SizeUnit::Percent;
return 100;
}
if ( unitString.length() > 0 )
{
if ( unitString.at(0) == '%' )
*unit = PartitionLayout::SizeUnit::Percent;
else if ( unitString.at(0).toUpper() == 'K' )
*unit = PartitionLayout::SizeUnit::KiB;
else if ( unitString.at(0).toUpper() == 'M' )
*unit = PartitionLayout::SizeUnit::MiB;
else if ( unitString.at(0).toUpper() == 'G' )
*unit = PartitionLayout::SizeUnit::GiB;
else
*unit = PartitionLayout::SizeUnit::Byte;
}
else
{
*unit = PartitionLayout::SizeUnit::Byte;
}
return value;
m_partLayout.append( entry );
}
PartitionLayout::PartitionEntry::PartitionEntry(const QString& size, const QString& min)
{
partSize = parseSizeString( size , &partSizeUnit );
partSize = PartUtils::parseSizeString( size , &partSizeUnit );
if ( !min.isEmpty() )
partMinSize = parseSizeString( min , &partMinSizeUnit );
partMinSize = PartUtils::parseSizeString( min , &partMinSizeUnit );
}
void
@ -115,9 +88,9 @@ PartitionLayout::addEntry( const QString& mountPoint, const QString& size, const
PartitionLayout::PartitionEntry entry( size, min );
entry.partMountPoint = mountPoint;
entry.partFileSystem = FileSystem::Ext4;
entry.partFileSystem = m_defaultFsType;
partLayout.append( entry );
m_partLayout.append( entry );
}
void
@ -127,38 +100,11 @@ PartitionLayout::addEntry( const QString& label, const QString& mountPoint, cons
entry.partLabel = label;
entry.partMountPoint = mountPoint;
entry.partFileSystem = FileSystem::typeForName( fs );
PartUtils::findFS( fs, &entry.partFileSystem );
if ( entry.partFileSystem == FileSystem::Unknown )
entry.partFileSystem = m_defaultFsType;
partLayout.append( entry );
}
static qint64
sizeToSectors( double size, PartitionLayout::SizeUnit unit, qint64 totalSize, qint64 logicalSize )
{
qint64 sectors;
double tmp;
if ( unit == PartitionLayout::SizeUnit::Percent )
{
tmp = static_cast<double>( totalSize ) * size / 100;
sectors = static_cast<qint64>( tmp );
}
else
{
tmp = size;
if ( unit >= PartitionLayout::SizeUnit::KiB )
tmp *= 1024;
if ( unit >= PartitionLayout::SizeUnit::MiB )
tmp *= 1024;
if ( unit >= PartitionLayout::SizeUnit::GiB )
tmp *= 1024;
sectors = PartitionActions::bytesToSectors( static_cast<unsigned long long>( tmp ),
logicalSize
);
}
return sectors;
m_partLayout.append( entry );
}
QList< Partition* >
@ -175,13 +121,13 @@ PartitionLayout::execute( Device *dev, qint64 firstSector,
// TODO: Refine partition sizes to make sure there is room for every partition
// Use a default (200-500M ?) minimum size for partition without minSize
foreach( const PartitionLayout::PartitionEntry& part, partLayout )
foreach( const PartitionLayout::PartitionEntry& part, m_partLayout )
{
Partition *currentPartition = nullptr;
// Calculate partition size
size = sizeToSectors( part.partSize, part.partSizeUnit, totalSize, dev->logicalSize() );
minSize = sizeToSectors( part.partMinSize, part.partMinSizeUnit, totalSize, dev->logicalSize() );
size = PartUtils::sizeToSectors( part.partSize, part.partSizeUnit, totalSize, dev->logicalSize() );
minSize = PartUtils::sizeToSectors( part.partMinSize, part.partMinSizeUnit, totalSize, dev->logicalSize() );
if ( size < minSize )
size = minSize;
if ( size > availableSize )
@ -194,7 +140,7 @@ PartitionLayout::execute( Device *dev, qint64 firstSector,
parent,
*dev,
role,
static_cast<FileSystem::Type>(part.partFileSystem),
part.partFileSystem,
firstSector,
end,
PartitionTable::FlagNone
@ -206,7 +152,7 @@ PartitionLayout::execute( Device *dev, qint64 firstSector,
parent,
*dev,
role,
static_cast<FileSystem::Type>(part.partFileSystem),
part.partFileSystem,
firstSector,
end,
luksPassphrase,

View File

@ -20,10 +20,13 @@
#ifndef PARTITIONLAYOUT_H
#define PARTITIONLAYOUT_H
#include "core/PartUtils.h"
#include "Typedefs.h"
// KPMcore
#include <kpmcore/core/partitiontable.h>
#include <kpmcore/fs/filesystem.h>
// Qt
#include <QList>
@ -35,24 +38,15 @@ class PartitionLayout
{
public:
enum SizeUnit
{
Percent = 0,
Byte,
KiB,
MiB,
GiB
};
struct PartitionEntry
{
QString partLabel;
QString partMountPoint;
int partFileSystem = 0;
FileSystem::Type partFileSystem = FileSystem::Unknown;
double partSize = 0.0L;
SizeUnit partSizeUnit = Percent;
PartUtils::SizeUnit partSizeUnit = PartUtils::SizeUnit::Percent;
double partMinSize = 0.0L;
SizeUnit partMinSizeUnit = Percent;
PartUtils::SizeUnit partMinSizeUnit = PartUtils::SizeUnit::Percent;
/// @brief All-zeroes PartitionEntry
PartitionEntry() {};
@ -76,7 +70,8 @@ public:
QList< Partition* > execute( Device *dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role );
private:
QList< PartitionEntry > partLayout;
FileSystem::Type m_defaultFsType;
QList< PartitionEntry > m_partLayout;
};
#endif /* PARTITIONLAYOUT_H */

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 );
@ -1248,7 +1243,7 @@ ChoicePage::setupActions()
}
if ( PartUtils::canBeReplaced( *it ) )
{
cDebug() << ".. contains replacable" << it;
cDebug() << ".. contains replaceable" << it;
atLeastOneCanBeReplaced = true;
}
if ( (*it)->isMounted() )

View File

@ -34,7 +34,7 @@ CreateVolumeGroupDialog::CreateVolumeGroupDialog( QString& vgName,
, m_selectedPVs( selectedPVs )
, m_peSize( pSize )
{
setWindowTitle( "Create Volume Group" );
setWindowTitle( tr( "Create Volume Group" ) );
peSize()->setValue( pSize );

View File

@ -44,7 +44,7 @@ void
standardMountPoints(QComboBox& combo)
{
combo.clear();
combo.addItem( combo.tr( "(no mount point)" ) );
combo.addItem( QObject::tr( "(no mount point)" ) );
combo.addItems( standardMountPoints() );
}

View File

@ -56,7 +56,6 @@
#include <kpmcore/ops/removevolumegroupoperation.h>
// Qt
#include <QDebug>
#include <QHeaderView>
#include <QItemSelectionModel>
#include <QMessageBox>
@ -90,22 +89,9 @@ PartitionPage::PartitionPage( PartitionCoreModule* core, QWidget* parent )
updateFromCurrentDevice();
connect( m_ui->deviceComboBox, &QComboBox::currentTextChanged,
[ this ]( const QString& /* text */ )
{
updateFromCurrentDevice();
} );
connect( m_ui->bootLoaderComboBox, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::activated),
[ this ]( const QString& /* text */ )
{
m_lastSelectedBootLoaderIndex = m_ui->bootLoaderComboBox->currentIndex();
} );
connect( m_ui->bootLoaderComboBox, &QComboBox::currentTextChanged,
[ this ]( const QString& /* text */ )
{
updateBootLoaderInstallPath();
} );
connect( m_ui->deviceComboBox, &QComboBox::currentTextChanged, this, &PartitionPage::updateFromCurrentDevice );
connect( m_ui->bootLoaderComboBox, QOverload<int>::of(&QComboBox::activated), this, &PartitionPage::updateSelectedBootLoaderIndex );
connect( m_ui->bootLoaderComboBox, &QComboBox::currentTextChanged, this, &PartitionPage::updateBootLoaderInstallPath );
connect( m_core, &PartitionCoreModule::isDirtyChanged, m_ui->revertButton, &QWidget::setEnabled );
@ -376,18 +362,18 @@ PartitionPage::onCreateClicked()
if ( !checkCanCreate( model->device() ) )
return;
QPointer< CreatePartitionDialog > dlg = new CreatePartitionDialog( model->device(),
partition->parent(),
nullptr,
getCurrentUsedMountpoints(),
this );
dlg->initFromFreeSpace( partition );
if ( dlg->exec() == QDialog::Accepted )
CreatePartitionDialog dlg(
model->device(),
partition->parent(),
nullptr,
getCurrentUsedMountpoints(),
this );
dlg.initFromFreeSpace( partition );
if ( dlg.exec() == QDialog::Accepted )
{
Partition* newPart = dlg->createPartition();
m_core->createPartition( model->device(), newPart, dlg->newFlags() );
Partition* newPart = dlg.createPartition();
m_core->createPartition( model->device(), newPart, dlg.newFlags() );
}
delete dlg;
}
void
@ -508,10 +494,17 @@ PartitionPage::updateBootLoaderInstallPath()
QVariant var = m_ui->bootLoaderComboBox->currentData( BootLoaderModel::BootLoaderPathRole );
if ( !var.isValid() )
return;
qDebug() << "PartitionPage::updateBootLoaderInstallPath" << var.toString();
cDebug() << "PartitionPage::updateBootLoaderInstallPath" << var.toString();
m_core->setBootLoaderInstallPath( var.toString() );
}
void
PartitionPage::updateSelectedBootLoaderIndex()
{
m_lastSelectedBootLoaderIndex = m_ui->bootLoaderComboBox->currentIndex();
cDebug() << "Selected bootloader index" << m_lastSelectedBootLoaderIndex;
}
void
PartitionPage::updateFromCurrentDevice()
{
@ -581,7 +574,7 @@ void
PartitionPage::onPartitionModelReset()
{
m_ui->partitionTreeView->expandAll();
// updateButtons();
updateButtons();
updateBootLoaderIndex();
}

View File

@ -50,6 +50,14 @@ public:
int selectedDeviceIndex();
void selectDeviceByIndex( int index );
private slots:
/// @brief Update everything when the base device changes
void updateFromCurrentDevice();
/// @brief Update when the selected device for boot loader changes
void updateBootLoaderInstallPath();
/// @brief Explicitly selected boot loader path
void updateSelectedBootLoaderIndex();
private:
QScopedPointer< Ui_PartitionPage > m_ui;
PartitionCoreModule* m_core;
@ -67,8 +75,6 @@ private:
void updatePartitionToCreate( Device*, Partition* );
void editExistingPartition( Device*, Partition* );
void updateBootLoaderInstallPath();
void updateFromCurrentDevice();
void updateBootLoaderIndex();
/**

View File

@ -3,7 +3,7 @@
* Copyright 2014, Aurélien Gâteau <agateau@kde.org>
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -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 "
@ -493,55 +478,6 @@ nameToChoice( QString name, bool& ok )
return names.find( name, ok );
}
/** @brief translate @p defaultFS into a recognized name
*
* Makes several attempts to translate the string into a
* name that KPMCore will recognize.
*/
static QString
findFS( QString defaultFS )
{
QStringList fsLanguage { QLatin1Literal( "C" ) }; // Required language list to turn off localization
if ( defaultFS.isEmpty() )
{
cWarning() << "Partition-module setting *defaultFileSystemType* is missing, using ext4";
defaultFS = QStringLiteral( "ext4" );
}
if ( FileSystem::typeForName( defaultFS, fsLanguage ) != FileSystem::Unknown )
{
cDebug() << "Partition-module setting *defaultFileSystemType*" << defaultFS;
return defaultFS;
}
// Second pass: try case-insensitive
const auto fstypes = FileSystem::types();
for ( FileSystem::Type t : fstypes )
{
if ( 0 == QString::compare( defaultFS, FileSystem::nameForType( t, fsLanguage ), Qt::CaseInsensitive ) )
{
defaultFS = FileSystem::nameForType( t, fsLanguage );
cWarning() << "Partition-module setting *defaultFileSystemType* changed" << defaultFS;
return defaultFS;
}
}
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << defaultFS << ") using ext4.";
defaultFS = QStringLiteral( "ext4" );
#ifdef DEBUG_FILESYSTEMS
// This bit is for distro's debugging their settings, and shows
// all the strings that KPMCore is matching against for FS type.
{
Logger::CDebug d;
using TR = Logger::DebugRow< int, QString >;
const auto fstypes = FileSystem::types();
d << "Available types (" << fstypes.count() << ')';
for ( FileSystem::Type t : fstypes )
d << TR( static_cast<int>( t ), FileSystem::nameForType( t, fsLanguage ) );
}
#endif
return defaultFS;
}
void
PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
@ -553,6 +489,17 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
efiSP = QStringLiteral( "/boot/efi" );
gs->insert( "efiSystemPartition", efiSP );
// Set up firmwareType global storage entry. This is used, e.g. by the bootloader module.
QString firmwareType( PartUtils::isEfiSystem() ? QStringLiteral( "efi" ) : QStringLiteral( "bios" ) );
cDebug() << "Setting firmwareType to" << firmwareType;
gs->insert( "firmwareType", firmwareType );
// Read and parse key efiSystemPartitionSize
if ( configurationMap.contains( "efiSystemPartitionSize" ) )
{
gs->insert( "efiSystemPartitionSize", CalamaresUtils::getString( configurationMap, "efiSystemPartitionSize" ) );
}
// SWAP SETTINGS
//
// This is a bit convoluted because there's legacy settings to handle as well
@ -630,23 +577,38 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
gs->insert( "alwaysShowPartitionLabels", CalamaresUtils::getBool( configurationMap, "alwaysShowPartitionLabels", true ) );
gs->insert( "enableLuksAutomatedPartitioning", CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) );
gs->insert( "allowManualPartitioning", CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ) );
gs->insert( "defaultFileSystemType", findFS( CalamaresUtils::getString( configurationMap, "defaultFileSystemType" ) ) );
// The defaultFileSystemType setting needs a bit more processing,
// as we want to cover various cases (such as different cases)
QString fsName = CalamaresUtils::getString( configurationMap, "defaultFileSystemType" );
FileSystem::Type fsType;
if ( fsName.isEmpty() )
cWarning() << "Partition-module setting *defaultFileSystemType* is missing, will use ext4";
QString fsRealName = PartUtils::findFS( fsName, &fsType );
if ( fsRealName == fsName )
cDebug() << "Partition-module setting *defaultFileSystemType*" << fsRealName;
else if ( fsType != FileSystem::Unknown )
cWarning() << "Partition-module setting *defaultFileSystemType* changed" << fsRealName;
else
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << fsRealName << ") using ext4.";
gs->insert( "defaultFileSystemType", fsRealName );
// 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" ) )
{
@ -665,5 +627,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

@ -35,7 +35,7 @@ ResizeVolumeGroupDialog::ResizeVolumeGroupDialog( LvmDevice *device,
: VolumeGroupBaseDialog( device->name(), device->physicalVolumes(), parent )
, m_selectedPVs( selectedPVs )
{
setWindowTitle( "Resize Volume Group" );
setWindowTitle( tr( "Resize Volume Group" ) );
for ( int i = 0; i < pvList()->count(); i++ )
pvList()->item(i)->setCheckState( Qt::Checked );

View File

@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
<string>VolumeGroupDialog</string>
<string>Create Volume Group</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">

View File

@ -27,6 +27,7 @@
class CreateVolumeGroupJob : public Calamares::Job
{
Q_OBJECT
public:
CreateVolumeGroupJob( QString& vgName, QVector< const Partition* > pvList, const qint32 peSize );

View File

@ -25,6 +25,7 @@ class LvmDevice;
class DeactivateVolumeGroupJob : public Calamares::Job
{
Q_OBJECT
public:
DeactivateVolumeGroupJob( LvmDevice* device );

View File

@ -25,6 +25,7 @@ class LvmDevice;
class RemoveVolumeGroupJob : public Calamares::Job
{
Q_OBJECT
public:
RemoveVolumeGroupJob( LvmDevice* device );

View File

@ -28,6 +28,7 @@ class Partition;
class ResizeVolumeGroupJob : public Calamares::Job
{
Q_OBJECT
public:
ResizeVolumeGroupJob( LvmDevice* device, QVector< const Partition* >& partitionList );

View File

@ -3,6 +3,10 @@
# etc.) use just /boot.
efiSystemPartition: "/boot/efi"
# This optional setting specifies the size of the EFI system partition.
# If nothing is specified, the default size of 300MiB will be used.
# efiSystemPartitionSize: 300M
# In autogenerated partitioning, allow the user to select a swap size?
# If there is exactly one choice, no UI is presented, and the user
# cannot make a choice -- this setting is used. If there is more than

View File

@ -350,7 +350,8 @@ PartitionJobTests::testResizePartition()
// Make the test data file smaller than the full size of the partition to
// accomodate for the file system overhead
const QByteArray testData = generateTestData( CalamaresUtils::MiBtoBytes( qMin( oldSizeMB, newSizeMB ) ) * 3 / 4 );
const unsigned long long minSizeMB = qMin( oldSizeMB, newSizeMB );
const QByteArray testData = generateTestData( CalamaresUtils::MiBtoBytes( minSizeMB ) * 3 / 4 );
const QString testName = "test.data";
// Setup: create the test partition

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

@ -6,6 +6,7 @@
# Copyright 2016, Artoo <artoo@manjaro.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org>
# Copyright 2018, Gabriel Craciunescu <crazy@frugalware.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
@ -24,6 +25,16 @@ import libcalamares
from libcalamares.utils import debug, target_env_call
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configure Plymouth theme")
class PlymouthController:

View File

@ -5,6 +5,7 @@
#
# Copyright 2015, Teo Mrnjavac <teo@kde.org>
# Copyright 2017. Alf Gaida <agaida@siduction.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
@ -22,6 +23,16 @@
import subprocess
import libcalamares
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Remove live user from target system")
def run():
"""

View File

@ -6,7 +6,7 @@
# Copyright 2016, Artoo <artoo@manjaro.org>
# Copyright 2017, Philip Müller <philm@manjaro.org>
# Copyright 2018, Artoo <artoo@artixlinux.org>
# Copyright 2018, Adriaan de Groot <groot@kde.org>
# Copyright 2018-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
@ -27,6 +27,17 @@ from libcalamares.utils import target_env_call, warning
from os.path import exists, join
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configure OpenRC services")
class OpenrcController:
"""
This is the openrc service controller.
@ -45,6 +56,22 @@ class OpenrcController:
self.initdDir = libcalamares.job.configuration['initdDir']
self.runlevelsDir = libcalamares.job.configuration['runlevelsDir']
def make_failure_description(self, state, name, runlevel):
"""
Returns a generic "could not <foo>" failure message, specialized
for the action @p state and the specific service @p name in @p runlevel.
"""
if state == "add":
description = _("Cannot add service {name!s} to run-level {level!s}.")
elif state == "del":
description = _("Cannot remove service {name!s} from run-level {level!s}.")
else:
description = _("Unknown service-action <code>{arg!s}</code> for service {name!s} in run-level {level!s}.")
return description.format(arg=state, name=name, level=runlevel)
def update(self, state):
"""
Call rc-update for each service listed
@ -69,24 +96,31 @@ class OpenrcController:
if exists(runlevel_path):
ec = target_env_call(["rc-update", state, name, runlevel])
if ec != 0:
warning("Cannot {} service {} to {}".format(state, name, runlevel))
warning("rc-update returned error code {!s}".format(ec))
if mandatory:
return ("Cannot {} service {} to {}".format(state, name, runlevel),
"rc-update {} call in chroot returned error code {}".format(state, ec)
title = _("Cannot modify service")
diagnostic = _("<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}.").format(arg=state, num=ec)
return (title,
self.make_failure_description(state, name, runlevel) + " " + diagnostic
)
else:
warning("Could not {} service {} in {}, error {!s}".format(state, name, runlevel, ec))
else:
warning("Target runlevel {} does not exist for {}.".format(runlevel, name))
if mandatory:
return ("Target runlevel {} does not exist for {}.".format(runlevel, name),
"No {} found.".format(runlevel_path))
else:
warning("Target runlevel {} does not exist for {}.".format(runlevel, name))
title = _("Target runlevel does not exist")
diagnostic = _("The path for runlevel {level!s} is <code>{path!s}</code>, which does not exist.").format(level=runlevel, path=runlevel_path)
return (title,
self.make_failure_description(state, name, runlevel) + " " + diagnostic
)
else:
warning("Target service {} does not exist in {}.".format(name, self.initdDir))
if mandatory:
return ("Target service {} does not exist.".format(name),
"No {} found.".format(service_path))
else:
warning("Target service {} does not exist in {}.".format(name, self.initdDir))
title = _("Target service does not exist")
diagnostic = _("The path for service {name!s} is <code>{path!s}</code>, which does not exist.").format(name=name, path=service_path)
return (title,
self.make_failure_description(state, name, runlevel) + " " + diagnostic
)
def run(self):

View File

@ -6,7 +6,7 @@
# Copyright 2014, Philip Müller <philm@manjaro.org>
# Copyright 2014, Teo Mrnjavac <teo@kde.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org>
# Copyright 2018, Adriaan de Groot <groot@kde.org>
# Copyright 2018-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
@ -24,6 +24,17 @@
import libcalamares
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configure systemd services")
def systemctl(targets, command, suffix):
"""
For each entry in @p targets, run "systemctl <command> <thing>",
@ -47,17 +58,32 @@ def systemctl(targets, command, suffix):
)
if ec != 0:
libcalamares.utils.warning(
"Cannot {} systemd {} {}".format(command, suffix, name)
)
libcalamares.utils.warning(
"systemctl {} call in chroot returned error code {}".format(command, ec)
)
if mandatory:
return ("Cannot {} systemd {} {}".format(command, suffix, name),
"systemctl {} call in chroot returned error code {}".format(command, ec)
title = _("Cannot modify service")
diagnostic = _("<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}.").format(arg=command, num=ec)
if command == "enable" and suffix == ".service":
description = _("Cannot enable systemd service <code>{name!s}</code>.")
elif command == "enable" and suffix == ".target":
description = _("Cannot enable systemd target <code>{name!s}</code>.")
elif command == "disable" and suffix == ".service":
description = _("Cannot enable systemd service <code>{name!s}</code>.")
elif command == "disable" and suffix == ".target":
description = _("Cannot disable systemd target <code>{name!s}</code>.")
elif command == "mask":
description = _("Cannot mask systemd unit <code>{name!s}</code>.")
else:
description = _("Unknown systemd commands <code>{command!s}</code> and <code>{suffix!s}</code> for unit {name!s}.")
return (title,
description.format(name=name, command=command, suffix=suffix) + " " + diagnostic
)
else:
libcalamares.utils.warning(
"Cannot {} systemd {} {}".format(command, suffix, name)
)
libcalamares.utils.warning(
"systemctl {} call in chroot returned error code {}".format(command, ec)
)
return None
@ -92,6 +118,4 @@ def run():
if r is not None:
return r
# This could have just been return r
return None

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

@ -154,29 +154,29 @@ CreateUserJob::exec()
useradd << "-c" << m_fullName;
useradd << m_userName;
auto pres = CalamaresUtils::System::instance()->targetEnvCommand( useradd );
if ( pres.getExitCode() )
auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( useradd );
if ( commandResult.getExitCode() )
{
cError() << "useradd failed" << pres.getExitCode();
return pres.explainProcess( useradd, 10 /* bogus timeout */ );
cError() << "useradd failed" << commandResult.getExitCode();
return commandResult.explainProcess( useradd, 10 /* bogus timeout */ );
}
pres = CalamaresUtils::System::instance()->targetEnvCommand(
commandResult = CalamaresUtils::System::instance()->targetEnvCommand(
{ "usermod", "-aG", defaultGroups, m_userName } );
if ( pres.getExitCode() )
if ( commandResult.getExitCode() )
{
cError() << "usermod failed" << pres.getExitCode();
return pres.explainProcess( "usermod", 10 );
cError() << "usermod failed" << commandResult.getExitCode();
return commandResult.explainProcess( "usermod", 10 );
}
QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName );
QString homeDir = QString( "/home/%1" ).arg( m_userName );
pres = CalamaresUtils::System::instance()->targetEnvCommand(
commandResult = CalamaresUtils::System::instance()->targetEnvCommand(
{ "chown", "-R", userGroup, homeDir } );
if ( pres.getExitCode() )
if ( commandResult.getExitCode() )
{
cError() << "chown failed" << pres.getExitCode();
return pres.explainProcess( "chown", 10 );
cError() << "chown failed" << commandResult.getExitCode();
return commandResult.explainProcess( "chown", 10 );
}
return Calamares::JobResult::ok();

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,15 +378,7 @@ RequirementsChecker::checkHasInternet()
bool
RequirementsChecker::checkIsRoot()
GeneralRequirements::checkIsRoot()
{
return !geteuid();
}
void
RequirementsChecker::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;
@ -74,15 +45,10 @@ private:
bool checkHasPower();
bool checkHasInternet();
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.