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

This commit is contained in:
Philip Müller 2020-03-27 13:09:00 +01:00
commit af4a7086df
42 changed files with 1660 additions and 411 deletions

View File

@ -7,6 +7,16 @@ charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
[CMakeLists.txt]
indent_style = space
indent_size = 4
insert_final_newline = true
[*.cmake]
indent_style = space
indent_size = 4
insert_final_newline = true
[*.{py,cpp,h}]
indent_style = space
indent_size = 4

View File

@ -6,6 +6,7 @@ website will have to do for older versions.
# 3.2.21 (unreleased) #
This release contains contributions from (alphabetically by first name):
- Anke Boersma
- Camilo Higuita
- Gabriel Craciunescu
- Gaël PORTAY
@ -25,6 +26,11 @@ This release contains contributions from (alphabetically by first name):
more configurable: the branding key *sidebar* controls it. The sidebar
can be shown as a widget (default, as it has been), hidden, or use a
new QML view which is more easily customised.
- A new `settings.conf` key *quit-at-end* will automatically close
Calamares (by clicking on the *Done* button) when the end of the
sequence is reached. If *finished* is the last module in the sequence,
this will run whatever it is configured for; you can also leave out
the finished page and Calamares will close after the exec parts.
## Modules ##
- *packages* now reports more details in the installation progress-bar.

View File

@ -181,3 +181,11 @@ disable-cancel: false
#
# YAML: boolean.
disable-cancel-during-exec: false
# If this is set to true, then once the end of the sequence has
# been reached, the quit (done) button is clicked automatically
# and Calamares will close. Default is false: the user will see
# that the end of installation has been reached, and that things are ok.
#
#
quit-at-end: false

View File

@ -6,26 +6,72 @@ In principle, all parts can be styled through CSS.
Missing parts should be filed as issues.
The IDs are based on the object names in the C++ code.
You can use the Debug Dialog to find out object names:
- Open the debug dialog
- Choose tab *Tools*
- Click *Widget Tree* button
The list of object names is printed in the log.
Documentation for styling Qt Widgets through a stylesheet
can be found at
https://doc.qt.io/qt-5/stylesheet-examples.html
https://doc.qt.io/qt-5/stylesheet-reference.html
In Calamares, styling widget classes is supported (e.g.
using `QComboBox` as a selector). You can also use specific
object names (ids), which you can find through debugging tools.
using `QComboBox` as a selector).
This example stylesheet has all the actual styling commented out.
The examples are not exhaustive.
*/
/* Main application window.
/*** Generic Widgets.
*
* You can style **all** widgets of a given class by selecting
* the class name. Some widgets have specialized sub-selectors.
*/
/*
QPushButton { background-color: green; }
*/
/*** Main application window.
*
* The main application window has the sidebar, which in turn
* contains a logo and a list of items -- note that the list
* can **not** be styled, since it has its own custom C++
* delegate code.
*/
/*
#mainApp { }
#logoApp { }
#sidebarApp { }
#sidebarMenuApp { }
#logoApp { }
*/
/* Partitioning module.
/*** Welcome module.
*
* There are plenty of parts, but the buttons are the most interesting
* ones (donate, release notes, ...). The little icon image can be
* styled through *qproperty-icon*, which is a little obscure.
* URLs can reference the QRC paths of the Calamares application
* or loaded via plugins or within the filesystem. There is no
* comprehensive list of available icons, though.
*/
/*
QPushButton#aboutButton { qproperty-icon: url(:/data/images/release.svg); }
#donateButton,
#supportButton,
#releaseNotesButton,
#knownIssuesButton { qproperty-icon: url(:/data/images/help.svg); }
*/
/*** Partitioning module.
*
* Many moving parts, which you will need to experiment with.
*/
/*
#bootInfoIcon { }
#bootInfoLable { }
#deviceInfoIcon { }
@ -34,8 +80,13 @@ object names (ids), which you can find through debugging tools.
#partitionBarView { }
*/
/* Licensing module.
/*** Licensing module.
*
* The licensing module paints individual widgets for each of
* the licenses. The item can be collapsed or expanded.
*/
/*
#licenseItem { }
#licenseItemFullText { }
*/

View File

@ -83,7 +83,7 @@ CalamaresApplication::init()
initQmlPath();
initBranding();
CalamaresUtils::installTranslator( QLocale::system(), QString(), this );
CalamaresUtils::installTranslator( QLocale::system(), QString() );
setQuitOnLastWindowClosed( false );
setWindowIcon( QIcon( Calamares::Branding::instance()->imagePath( Calamares::Branding::ProductIcon ) ) );

View File

@ -69,6 +69,7 @@ public:
if ( anyFailed && !job->isEmergency() )
{
cDebug() << "Skipping non-emergency job" << job->prettyName();
++m_jobIndex;
continue;
}
@ -83,11 +84,9 @@ public:
message = result.message();
details = result.details();
}
if ( !anyFailed )
{
emitProgress( 1.0 );
++m_jobIndex;
}
}
if ( anyFailed )
{
emitFailed( message, details );
@ -141,7 +140,7 @@ private:
m_queue, "failed", Qt::QueuedConnection, Q_ARG( QString, message ), Q_ARG( QString, details ) );
}
void emitFinished() { QMetaObject::invokeMethod( m_queue, "finished", Qt::QueuedConnection ); }
void emitFinished() { QMetaObject::invokeMethod( m_queue, "finish", Qt::QueuedConnection ); }
};
JobThread::~JobThread() {}
@ -196,6 +195,7 @@ JobQueue::start()
Q_ASSERT( !m_thread->isRunning() );
m_thread->setJobs( std::move( m_jobs ) );
m_jobs.clear();
m_finished = false;
m_thread->start();
}
@ -217,4 +217,11 @@ JobQueue::enqueue( const JobList& jobs )
emit queueChanged( m_jobs );
}
void
JobQueue::finish()
{
m_finished = true;
emit finished();
}
} // namespace Calamares

View File

@ -45,6 +45,11 @@ public:
void enqueue( const JobList& jobs );
void start();
bool isRunning() const { return !m_finished; }
public slots:
void finish();
signals:
void queueChanged( const JobList& jobs );
void progress( qreal percent, const QString& prettyName );
@ -57,6 +62,7 @@ private:
JobList m_jobs;
JobThread* m_thread;
GlobalStorage* m_storage;
bool m_finished = true; ///< Initially, not running
};
} // namespace Calamares

View File

@ -232,6 +232,7 @@ Settings::Settings( const QString& settingsFilePath, bool debugMode )
m_isSetupMode = requireBool( config, "oem-setup", !m_doChroot );
m_disableCancel = requireBool( config, "disable-cancel", false );
m_disableCancelDuringExec = requireBool( config, "disable-cancel-during-exec", false );
m_quitAtEnd = requireBool( config, "quit-at-end", false );
}
catch ( YAML::Exception& e )
{

View File

@ -101,6 +101,9 @@ public:
/** @brief Temporary setting of disable-cancel: can't cancel during exec. */
bool disableCancelDuringExec() const { return m_disableCancelDuringExec; }
/** @brief Is quit-at-end set? (Quit automatically when done) */
bool quitAtEnd() const { return m_quitAtEnd; }
private:
static Settings* s_instance;
@ -117,6 +120,7 @@ private:
bool m_promptInstall;
bool m_disableCancel;
bool m_disableCancelDuringExec;
bool m_quitAtEnd;
};
} // namespace Calamares

View File

@ -33,9 +33,7 @@ Label::Label( const QString& locale, LabelFormat format, QObject* parent )
: QObject( parent )
, m_locale( Label::getLocale( locale ) )
, m_localeId( locale.isEmpty() ? m_locale.name() : locale )
{
//: language[name] (country[name])
QString longFormat = QObject::tr( "%1 (%2)" );
QString languageName = m_locale.nativeLanguageName();

View File

@ -40,10 +40,6 @@ class Label : public QObject
{
Q_OBJECT
Q_PROPERTY( QString label READ label CONSTANT FINAL )
Q_PROPERTY( QString englishLabel READ englishLabel CONSTANT FINAL )
Q_PROPERTY( QString localeId MEMBER m_localeId CONSTANT FINAL )
public:
/** @brief Formatting option for label -- add (country) to label. */
enum class LabelFormat
@ -65,6 +61,7 @@ public:
LabelFormat format = LabelFormat::IfNeededWithCountry,
QObject* parent = nullptr );
/** @brief Define a sorting order.
*
* Locales are sorted by their id, which means the ISO 2-letter code + country.

View File

@ -239,7 +239,13 @@ CStringListModel::CStringListModel( CStringPairList l )
{
}
CStringListModel::~CStringListModel() {}
void
CStringListModel::setList( CalamaresUtils::Locale::CStringPairList l )
{
beginResetModel();
m_list = l;
endResetModel();
}
int
CStringListModel::rowCount( const QModelIndex& ) const
@ -264,6 +270,30 @@ CStringListModel::data( const QModelIndex& index, int role ) const
return item ? ( role == Qt::DisplayRole ? item->tr() : item->key() ) : QVariant();
}
void
CStringListModel::setCurrentIndex( int index )
{
if ( ( index < 0 ) || ( index >= m_list.count() ) )
{
return;
}
m_currentIndex = index;
emit currentIndexChanged();
}
int
CStringListModel::currentIndex() const
{
return m_currentIndex;
}
QHash< int, QByteArray >
CStringListModel::roleNames() const
{
return { { Qt::DisplayRole, "label" }, { Qt::UserRole, "key" } };
}
const CStringPair*
CStringListModel::item( int index ) const
{

View File

@ -44,8 +44,9 @@ namespace Locale
* QPair<QString, QString> because there is API that needs
* C-style strings.
*/
class CStringPair
class CStringPair : public QObject
{
Q_OBJECT
public:
/// @brief An empty pair
CStringPair() {}
@ -86,6 +87,7 @@ public:
/// @brief A pair of strings for timezone regions (e.g. "America")
class TZRegion : public CStringPair
{
Q_OBJECT
public:
using CStringPair::CStringPair;
virtual ~TZRegion() override;
@ -117,6 +119,7 @@ private:
/// @brief A pair of strings for specific timezone names (e.g. "New_York")
class TZZone : public CStringPair
{
Q_OBJECT
public:
using CStringPair::CStringPair;
QString tr() const override;
@ -137,21 +140,52 @@ protected:
class CStringListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged )
public:
/// @brief Create empty model
CStringListModel();
CStringListModel() {}
/// @brief Create model from list (non-owning)
CStringListModel( CStringPairList );
virtual ~CStringListModel() override;
int rowCount( const QModelIndex& parent ) const override;
QVariant data( const QModelIndex& index, int role ) const override;
const CStringPair* item( int index ) const;
QHash< int, QByteArray > roleNames() const override;
void setCurrentIndex( int index );
int currentIndex() const;
void setList( CStringPairList );
inline int indexOf( const QString& key )
{
const auto it = std::find_if(
m_list.constBegin(), m_list.constEnd(), [&]( const CalamaresUtils::Locale::CStringPair* item ) -> bool {
return item->key() == key;
} );
if ( it != m_list.constEnd() )
{
// distance() is usually a long long
return int( std::distance( m_list.constBegin(), it ) );
}
else
{
return -1;
}
}
private:
CStringPairList m_list;
int m_currentIndex = -1;
signals:
void currentIndexChanged();
};
} // namespace Locale

View File

@ -276,7 +276,7 @@ Manager::synchronousGet( const QUrl& url, const RequestOptions& options )
}
QNetworkReply*
Manager::asynchronouseGet( const QUrl& url, const CalamaresUtils::Network::RequestOptions& options )
Manager::asynchronousGet( const QUrl& url, const CalamaresUtils::Network::RequestOptions& options )
{
return asynchronousRun( d->nam(), url, options );
}

View File

@ -146,7 +146,7 @@ public:
* This may be a nullptr if an error occurs immediately.
* The caller is responsible for cleaning up the reply (eventually).
*/
QNetworkReply* asynchronouseGet( const QUrl& url, const RequestOptions& options = RequestOptions() );
QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() );
private:
class Private;

View File

@ -197,7 +197,7 @@ static QTranslator* s_tztranslator = nullptr;
static QString s_translatorLocaleName;
void
installTranslator( const QLocale& locale, const QString& brandingTranslationsPrefix, QObject* )
installTranslator( const QLocale& locale, const QString& brandingTranslationsPrefix )
{
loadSingletonTranslator( BrandingLoader( locale, brandingTranslationsPrefix ), s_brandingTranslator );
loadSingletonTranslator( TZLoader( locale ), s_tztranslator );

View File

@ -36,9 +36,8 @@ namespace CalamaresUtils
* @brief installTranslator changes the application language.
* @param locale the new locale.
* @param brandingTranslationsPrefix the branding path prefix, from Calamares::Branding.
* @param parent the parent QObject.
*/
DLLEXPORT void installTranslator( const QLocale& locale, const QString& brandingTranslationsPrefix, QObject* parent );
DLLEXPORT void installTranslator( const QLocale& locale, const QString& brandingTranslationsPrefix );
DLLEXPORT QString translatorLocaleName();

View File

@ -29,8 +29,8 @@
#include "utils/Paste.h"
#include "utils/Retranslator.h"
#include "viewpages/BlankViewStep.h"
#include "viewpages/ViewStep.h"
#include "viewpages/ExecutionViewStep.h"
#include "viewpages/ViewStep.h"
#include <QApplication>
#include <QBoxLayout>
@ -309,6 +309,13 @@ stepIsExecute( const ViewStepList& steps, int index )
&& ( qobject_cast< ExecutionViewStep* >( steps.at( index ) ) != nullptr );
}
static inline bool
isAtVeryEnd( const ViewStepList& steps, int index )
{
return ( index >= steps.count() ) || ( index == steps.count() - 1 && steps.last()->isAtEnd() );
}
void
ViewManager::next()
{
@ -412,13 +419,17 @@ ViewManager::updateButtonLabels()
m_back->setText( tr( "&Back" ) );
// Cancel button changes label at the end
if ( isAtVeryEnd() )
if ( isAtVeryEnd( m_steps, m_currentStep ) )
{
m_quit->setText( tr( "&Done" ) );
m_quit->setToolTip( quitOnCompleteTooltip );
m_quit->setVisible( true ); // At end, always visible and enabled.
setButtonIcon( m_quit, "dialog-ok-apply" );
updateCancelEnabled( true );
if ( settings->quitAtEnd() )
{
m_quit->click();
}
}
else
{
@ -473,7 +484,7 @@ ViewManager::confirmCancelInstallation()
const auto* const settings = Calamares::Settings::instance();
// When we're at the very end, then it's always OK to exit.
if ( isAtVeryEnd() )
if ( isAtVeryEnd( m_steps, m_currentStep ) )
{
return true;
}

View File

@ -132,12 +132,6 @@ private:
void updateButtonLabels();
void updateCancelEnabled( bool enabled );
bool isAtVeryEnd() const
{
return ( m_currentStep >= m_steps.count() )
|| ( m_currentStep == m_steps.count() - 1 && m_steps.last()->isAtEnd() );
}
static ViewManager* s_instance;
ViewStepList m_steps;

View File

@ -136,7 +136,7 @@ ExecutionViewStep::isAtBeginning() const
bool
ExecutionViewStep::isAtEnd() const
{
return true;
return !JobQueue::instance()->isRunning();
}
void

View File

@ -102,7 +102,7 @@ def run():
status = _("Dummy python step {}").format(str(c) + ":" + repr(k))
libcalamares.utils.debug(_("Dummy python step {}").format(str(k)))
sleep(1)
libcalamares.job.setprogress(c * 1.0 / len(configlist))
libcalamares.job.setprogress(c * 1.0 / (len(configlist)+1))
c += 1
sleep(3)

View File

@ -18,11 +18,11 @@
#include "KeyboardViewStep.h"
#include "JobQueue.h"
#include "GlobalStorage.h"
#include "KeyboardPage.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
CALAMARES_PLUGIN_FACTORY_DEFINITION( KeyboardViewStepFactory, registerPlugin< KeyboardViewStep >(); )
KeyboardViewStep::KeyboardViewStep( QObject* parent )
@ -40,8 +40,10 @@ KeyboardViewStep::KeyboardViewStep( QObject* parent )
KeyboardViewStep::~KeyboardViewStep()
{
if ( m_widget && m_widget->parent() == nullptr )
{
m_widget->deleteLater();
}
}
QString
@ -111,9 +113,7 @@ void
KeyboardViewStep::onLeave()
{
m_widget->finalize();
m_jobs = m_widget->createJobs( m_xOrgConfFileName,
m_convertedKeymapPath,
m_writeEtcDefaultKeyboard );
m_jobs = m_widget->createJobs( m_xOrgConfFileName, m_convertedKeymapPath, m_writeEtcDefaultKeyboard );
m_prettyStatus = m_widget->prettyStatus();
}
@ -121,29 +121,35 @@ KeyboardViewStep::onLeave()
void
KeyboardViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
if ( configurationMap.contains( "xOrgConfFileName" ) &&
configurationMap.value( "xOrgConfFileName" ).type() == QVariant::String &&
!configurationMap.value( "xOrgConfFileName" ).toString().isEmpty() )
if ( configurationMap.contains( "xOrgConfFileName" )
&& configurationMap.value( "xOrgConfFileName" ).type() == QVariant::String
&& !configurationMap.value( "xOrgConfFileName" ).toString().isEmpty() )
{
m_xOrgConfFileName = configurationMap.value( "xOrgConfFileName" )
.toString();
m_xOrgConfFileName = configurationMap.value( "xOrgConfFileName" ).toString();
}
else
{
m_xOrgConfFileName = "00-keyboard.conf";
}
if ( configurationMap.contains( "convertedKeymapPath" ) &&
configurationMap.value( "convertedKeymapPath" ).type() == QVariant::String &&
!configurationMap.value( "convertedKeymapPath" ).toString().isEmpty() )
if ( configurationMap.contains( "convertedKeymapPath" )
&& configurationMap.value( "convertedKeymapPath" ).type() == QVariant::String
&& !configurationMap.value( "convertedKeymapPath" ).toString().isEmpty() )
{
m_convertedKeymapPath = configurationMap.value( "convertedKeymapPath" )
.toString();
m_convertedKeymapPath = configurationMap.value( "convertedKeymapPath" ).toString();
}
else
{
m_convertedKeymapPath = QString();
}
if ( configurationMap.contains( "writeEtcDefaultKeyboard" ) &&
configurationMap.value( "writeEtcDefaultKeyboard" ).type() == QVariant::Bool )
if ( configurationMap.contains( "writeEtcDefaultKeyboard" )
&& configurationMap.value( "writeEtcDefaultKeyboard" ).type() == QVariant::Bool )
{
m_writeEtcDefaultKeyboard = configurationMap.value( "writeEtcDefaultKeyboard" ).toBool();
}
else
{
m_writeEtcDefaultKeyboard = true;
}
}

View File

@ -20,12 +20,11 @@
#ifndef KEYBOARDVIEWSTEP_H
#define KEYBOARDVIEWSTEP_H
#include <QObject>
#include "DllMacro.h"
#include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h"
#include "DllMacro.h"
#include <QObject>
class KeyboardPage;
@ -48,7 +47,7 @@ public:
bool isAtBeginning() const override;
bool isAtEnd() const override;
QList< Calamares::job_ptr > jobs() const override;
Calamares::JobList jobs() const override;
void onActivate() override;
void onLeave() override;
@ -64,7 +63,7 @@ private:
QString m_convertedKeymapPath;
bool m_writeEtcDefaultKeyboard;
QList< Calamares::job_ptr > m_jobs;
Calamares::JobList m_jobs;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( KeyboardViewStepFactory )

View File

@ -13,6 +13,7 @@ calamares_add_plugin( locale
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
${geoip_src}
Config.cpp
LCLocaleDialog.cpp
LocaleConfiguration.cpp
LocalePage.cpp

View File

@ -0,0 +1,324 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
* Copyright 2020, Camilo Higuita <milo.h@aol.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
* 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 "Config.h"
#include "LCLocaleDialog.h"
#include "SetTimezoneJob.h"
#include "timezonewidget/timezonewidget.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "Settings.h"
#include "locale/Label.h"
#include "locale/TimeZone.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include <QDebug>
#include <QProcess>
Config::Config( QObject* parent )
: QObject( parent )
, m_regionList( CalamaresUtils::Locale::TZRegion::fromZoneTab() )
, m_regionModel( new CalamaresUtils::Locale::CStringListModel( m_regionList ) )
, m_zonesModel( new CalamaresUtils::Locale::CStringListModel() )
, m_blockTzWidgetSet( false )
{
connect( m_regionModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() {
m_zonesModel->setList( static_cast< const CalamaresUtils::Locale::TZRegion* >(
m_regionModel->item( m_regionModel->currentIndex() ) )
->zones() );
updateLocaleLabels();
} );
connect(
m_zonesModel, &CalamaresUtils::Locale::CStringListModel::currentIndexChanged, [&]() { updateLocaleLabels(); } );
}
Config::~Config()
{
qDeleteAll( m_regionList );
}
CalamaresUtils::Locale::CStringListModel*
Config::zonesModel() const
{
return m_zonesModel;
}
CalamaresUtils::Locale::CStringListModel*
Config::regionModel() const
{
return m_regionModel;
}
void
Config::setLocaleInfo( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath )
{
using namespace CalamaresUtils::Locale;
cDebug() << "REGION MODEL SIZE" << initialRegion << initialZone;
auto* region = m_regionList.find< TZRegion >( initialRegion );
if ( region && region->zones().find< TZZone >( initialZone ) )
{
this->m_regionModel->setCurrentIndex( m_regionModel->indexOf( initialRegion ) );
m_zonesModel->setList( region->zones() );
this->m_zonesModel->setCurrentIndex( m_zonesModel->indexOf( initialZone ) );
}
else
{
this->m_regionModel->setCurrentIndex( m_regionModel->indexOf( "America" ) );
m_zonesModel->setList(
static_cast< const TZRegion* >( m_regionModel->item( m_regionModel->currentIndex() ) )->zones() );
this->m_zonesModel->setCurrentIndex( m_zonesModel->indexOf( "New_York" ) );
}
// Some distros come with a meaningfully commented and easy to parse locale.gen,
// and others ship a separate file /usr/share/i18n/SUPPORTED with a clean list of
// supported locales. We first try that one, and if it doesn't exist, we fall back
// to parsing the lines from locale.gen
m_localeGenLines.clear();
QFile supported( "/usr/share/i18n/SUPPORTED" );
QByteArray ba;
if ( supported.exists() && supported.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
ba = supported.readAll();
supported.close();
const auto lines = ba.split( '\n' );
for ( const QByteArray& line : lines )
{
m_localeGenLines.append( QString::fromLatin1( line.simplified() ) );
}
}
else
{
QFile localeGen( localeGenPath );
if ( localeGen.open( QIODevice::ReadOnly | QIODevice::Text ) )
{
ba = localeGen.readAll();
localeGen.close();
}
else
{
cWarning() << "Cannot open file" << localeGenPath
<< ". Assuming the supported languages are already built into "
"the locale archive.";
QProcess localeA;
localeA.start( "locale", QStringList() << "-a" );
localeA.waitForFinished();
ba = localeA.readAllStandardOutput();
}
const auto lines = ba.split( '\n' );
for ( const QByteArray& line : lines )
{
if ( line.startsWith( "## " ) || line.startsWith( "# " ) || line.simplified() == "#" )
{
continue;
}
QString lineString = QString::fromLatin1( line.simplified() );
if ( lineString.startsWith( "#" ) )
{
lineString.remove( '#' );
}
lineString = lineString.simplified();
if ( lineString.isEmpty() )
{
continue;
}
m_localeGenLines.append( lineString );
}
}
if ( m_localeGenLines.isEmpty() )
{
cWarning() << "cannot acquire a list of available locales."
<< "The locale and localecfg modules will be broken as long as this "
"system does not provide"
<< "\n\t "
<< "* a well-formed" << supported.fileName() << "\n\tOR"
<< "* a well-formed"
<< ( localeGenPath.isEmpty() ? QLatin1String( "/etc/locale.gen" ) : localeGenPath ) << "\n\tOR"
<< "* a complete pre-compiled locale-gen database which allows complete locale -a output.";
return; // something went wrong and there's nothing we can do about it.
}
// Assuming we have a list of supported locales, we usually only want UTF-8 ones
// because it's not 1995.
for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); )
{
if ( !it->contains( "UTF-8", Qt::CaseInsensitive ) && !it->contains( "utf8", Qt::CaseInsensitive ) )
{
it = m_localeGenLines.erase( it );
}
else
{
++it;
}
}
// We strip " UTF-8" from "en_US.UTF-8 UTF-8" because it's redundant redundant.
for ( auto it = m_localeGenLines.begin(); it != m_localeGenLines.end(); ++it )
{
if ( it->endsWith( " UTF-8" ) )
{
it->chop( 6 );
}
*it = it->simplified();
}
updateGlobalStorage();
updateLocaleLabels();
}
void
Config::updateGlobalLocale()
{
auto* gs = Calamares::JobQueue::instance()->globalStorage();
const QString bcp47 = m_selectedLocaleConfiguration.toBcp47();
gs->insert( "locale", bcp47 );
}
void
Config::updateGlobalStorage()
{
auto* gs = Calamares::JobQueue::instance()->globalStorage();
const auto* location = currentLocation();
bool locationChanged = ( location->region() != gs->value( "locationRegion" ) )
|| ( location->zone() != gs->value( "locationZone" ) );
gs->insert( "locationRegion", location->region() );
gs->insert( "locationZone", location->zone() );
updateGlobalLocale();
// If we're in chroot mode (normal install mode), then we immediately set the
// timezone on the live system. When debugging timezones, don't bother.
#ifndef DEBUG_TIMEZONES
if ( locationChanged && Calamares::Settings::instance()->doChroot() )
{
QProcess::execute( "timedatectl", // depends on systemd
{ "set-timezone", location->region() + '/' + location->zone() } );
}
#endif
// Preserve those settings that have been made explicit.
auto newLocale = guessLocaleConfiguration();
if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lang )
{
newLocale.setLanguage( m_selectedLocaleConfiguration.language() );
}
if ( !m_selectedLocaleConfiguration.isEmpty() && m_selectedLocaleConfiguration.explicit_lc )
{
newLocale.lc_numeric = m_selectedLocaleConfiguration.lc_numeric;
newLocale.lc_time = m_selectedLocaleConfiguration.lc_time;
newLocale.lc_monetary = m_selectedLocaleConfiguration.lc_monetary;
newLocale.lc_paper = m_selectedLocaleConfiguration.lc_paper;
newLocale.lc_name = m_selectedLocaleConfiguration.lc_name;
newLocale.lc_address = m_selectedLocaleConfiguration.lc_address;
newLocale.lc_telephone = m_selectedLocaleConfiguration.lc_telephone;
newLocale.lc_measurement = m_selectedLocaleConfiguration.lc_measurement;
newLocale.lc_identification = m_selectedLocaleConfiguration.lc_identification;
}
newLocale.explicit_lang = m_selectedLocaleConfiguration.explicit_lang;
newLocale.explicit_lc = m_selectedLocaleConfiguration.explicit_lc;
m_selectedLocaleConfiguration = newLocale;
updateLocaleLabels();
}
void
Config::updateLocaleLabels()
{
LocaleConfiguration lc
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
auto labels = prettyLocaleStatus( lc );
emit prettyStatusChanged();
}
std::pair< QString, QString >
Config::prettyLocaleStatus( const LocaleConfiguration& lc ) const
{
using CalamaresUtils::Locale::Label;
Label lang( lc.language(), Label::LabelFormat::AlwaysWithCountry );
Label num( lc.lc_numeric, Label::LabelFormat::AlwaysWithCountry );
return std::make_pair< QString, QString >(
tr( "The system language will be set to %1." ).arg( lang.label() ),
tr( "The numbers and dates locale will be set to %1." ).arg( num.label() ) );
}
Calamares::JobList
Config::createJobs()
{
QList< Calamares::job_ptr > list;
const CalamaresUtils::Locale::TZZone* location = currentLocation();
Calamares::Job* j = new SetTimezoneJob( location->region(), location->zone() );
list.append( Calamares::job_ptr( j ) );
return list;
}
LocaleConfiguration
Config::guessLocaleConfiguration() const
{
return LocaleConfiguration::fromLanguageAndLocation(
QLocale().name(), m_localeGenLines, currentLocation() ? currentLocation()->country() : "" );
}
QMap< QString, QString >
Config::localesMap()
{
return m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration().toMap()
: m_selectedLocaleConfiguration.toMap();
}
QString
Config::prettyStatus() const
{
QString status;
status += tr( "Set timezone to %1/%2.<br/>" )
.arg( m_regionModel->item( m_regionModel->currentIndex() )->tr() )
.arg( m_zonesModel->item( m_zonesModel->currentIndex() )->tr() );
LocaleConfiguration lc
= m_selectedLocaleConfiguration.isEmpty() ? guessLocaleConfiguration() : m_selectedLocaleConfiguration;
auto labels = prettyLocaleStatus( lc );
status += labels.first + "<br/>";
status += labels.second + "<br/>";
return status;
}
const CalamaresUtils::Locale::TZZone*
Config::currentLocation() const
{
return static_cast< const CalamaresUtils::Locale::TZZone* >( m_zonesModel->item( m_zonesModel->currentIndex() ) );
}

View File

@ -0,0 +1,87 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020, Adriaan de Groot <groot@kde.org>
* Copyright 2020, Camilo Higuita <milo.h@aol.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
* 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 LOCALE_CONFIG_H
#define LOCALE_CONFIG_H
#include "LocaleConfiguration.h"
#include "timezonewidget/localeglobal.h"
#include "Job.h"
#include "locale/TimeZone.h"
#include <QAbstractListModel>
#include <QObject>
#include <memory>
class Config : public QObject
{
Q_OBJECT
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* zonesModel READ zonesModel CONSTANT FINAL )
Q_PROPERTY( CalamaresUtils::Locale::CStringListModel* regionModel READ regionModel CONSTANT FINAL )
Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL )
public:
Config( QObject* parent = nullptr );
~Config();
CalamaresUtils::Locale::CStringListModel* regionModel() const;
CalamaresUtils::Locale::CStringListModel* zonesModel() const;
void setLocaleInfo( const QString& initialRegion, const QString& initialZone, const QString& localeGenPath );
Calamares::JobList createJobs();
QMap< QString, QString > localesMap();
QString prettyStatus() const;
private:
CalamaresUtils::Locale::CStringPairList m_regionList;
CalamaresUtils::Locale::CStringListModel* m_regionModel;
CalamaresUtils::Locale::CStringListModel* m_zonesModel;
LocaleConfiguration m_selectedLocaleConfiguration;
QStringList m_localeGenLines;
int m_currentRegion = -1;
bool m_blockTzWidgetSet;
LocaleConfiguration guessLocaleConfiguration() const;
// For the given locale config, return two strings describing
// the settings for language and numbers.
std::pair< QString, QString > prettyLocaleStatus( const LocaleConfiguration& ) const;
/** @brief Update the GS *locale* key with the selected system language.
*
* This uses whatever is set in m_selectedLocaleConfiguration as the language,
* and writes it to GS *locale* key (as a string, in BCP47 format).
*/
void updateGlobalLocale();
void updateGlobalStorage();
void updateLocaleLabels();
const CalamaresUtils::Locale::TZZone* currentLocation() const;
signals:
void prettyStatusChanged();
};
#endif

View File

@ -0,0 +1,29 @@
# When debugging the timezone widget, add this debugging definition
# to have a debugging-friendly timezone widget, debug logging,
# and no intrusive timezone-setting while clicking around.
option( DEBUG_TIMEZONES "Debug-friendly timezone widget." OFF )
if( DEBUG_TIMEZONES )
add_definitions( -DDEBUG_TIMEZONES )
endif()
# Because we're sharing sources with the regular locale module
set( _locale ${CMAKE_CURRENT_SOURCE_DIR}/../locale )
include_directories( ${_locale} )
calamares_add_plugin( localeq
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
LocaleQmlViewStep.cpp
${_locale}/LocaleConfiguration.cpp
${_locale}/Config.cpp
${_locale}/SetTimezoneJob.cpp
${_locale}/timezonewidget/localeglobal.cpp
RESOURCES
${_locale}/locale.qrc
LINK_PRIVATE_LIBRARIES
calamaresui
Qt5::Network
SHARED_LIB
)

View File

@ -0,0 +1,203 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2018,2020 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 "LocaleQmlViewStep.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "geoip/Handler.h"
#include "network/Manager.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include "utils/Yaml.h"
#include "timezonewidget/localeglobal.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.h"
#include <QQmlEngine>
#include <QFutureWatcher>
#include <QPixmap>
#include <QVariant>
CALAMARES_PLUGIN_FACTORY_DEFINITION( LocaleQmlViewStepFactory, registerPlugin< LocaleQmlViewStep >(); )
LocaleQmlViewStep::LocaleQmlViewStep( QObject* parent )
: Calamares::QmlViewStep( parent )
, m_config( new Config( this ) )
, m_nextEnabled( false )
, m_geoip( nullptr )
{
emit nextStatusChanged( m_nextEnabled );
}
QObject*
LocaleQmlViewStep::getConfig()
{
return m_config;
}
void
LocaleQmlViewStep::fetchGeoIpTimezone()
{
if ( m_geoip && m_geoip->isValid() )
{
m_startingTimezone = m_geoip->get();
if ( !m_startingTimezone.isValid() )
{
cWarning() << "GeoIP lookup at" << m_geoip->url() << "failed.";
}
}
m_config->setLocaleInfo(m_startingTimezone.first, m_startingTimezone.second, m_localeGenPath);
}
Calamares::RequirementsList LocaleQmlViewStep::checkRequirements()
{
LocaleGlobal::init();
if ( m_geoip && m_geoip->isValid() )
{
auto& network = CalamaresUtils::Network::Manager::instance();
if ( network.hasInternet() )
{
fetchGeoIpTimezone();
}
else
{
if ( network.synchronousPing( m_geoip->url() ) )
{
fetchGeoIpTimezone();
}
}
}
return Calamares::RequirementsList();
}
QString
LocaleQmlViewStep::prettyName() const
{
return tr( "Location" );
}
bool
LocaleQmlViewStep::isNextEnabled() const
{
// TODO: should return true
return true;
}
bool
LocaleQmlViewStep::isBackEnabled() const
{
// TODO: should return true (it's weird that you are not allowed to have welcome *after* anything
return true;
}
bool
LocaleQmlViewStep::isAtBeginning() const
{
// TODO: adjust to "pages" in the QML
return true;
}
bool
LocaleQmlViewStep::isAtEnd() const
{
// TODO: adjust to "pages" in the QML
return true;
}
Calamares::JobList
LocaleQmlViewStep::jobs() const
{
return m_jobs;
}
void LocaleQmlViewStep::onActivate()
{
// TODO no sure if it is needed at all or for the abstract class to start something
}
void LocaleQmlViewStep::onLeave()
{
if ( true )
{
m_jobs = m_config->createJobs();
// m_prettyStatus = m_actualWidget->prettyStatus();
auto map = m_config->localesMap();
QVariantMap vm;
for ( auto it = map.constBegin(); it != map.constEnd(); ++it )
{
vm.insert( it.key(), it.value() );
}
Calamares::JobQueue::instance()->globalStorage()->insert( "localeConf", vm );
}
else
{
m_jobs.clear();
Calamares::JobQueue::instance()->globalStorage()->remove( "localeConf" );
}
}
void LocaleQmlViewStep::setConfigurationMap(const QVariantMap& configurationMap)
{
QString region = CalamaresUtils::getString( configurationMap, "region" );
QString zone = CalamaresUtils::getString( configurationMap, "zone" );
if ( !region.isEmpty() && !zone.isEmpty() )
{
m_startingTimezone = CalamaresUtils::GeoIP::RegionZonePair( region, zone );
}
else
{
m_startingTimezone
= CalamaresUtils::GeoIP::RegionZonePair( QStringLiteral( "America" ), QStringLiteral( "New_York" ) );
}
m_localeGenPath = CalamaresUtils::getString( configurationMap, "localeGenPath" );
if ( m_localeGenPath.isEmpty() )
{
m_localeGenPath = QStringLiteral( "/etc/locale.gen" );
}
bool ok = false;
QVariantMap geoip = CalamaresUtils::getSubMap( configurationMap, "geoip", ok );
if ( ok )
{
QString url = CalamaresUtils::getString( geoip, "url" );
QString style = CalamaresUtils::getString( geoip, "style" );
QString selector = CalamaresUtils::getString( geoip, "selector" );
m_geoip = std::make_unique< CalamaresUtils::GeoIP::Handler >( style, url, selector );
if ( !m_geoip->isValid() )
{
cWarning() << "GeoIP Style" << style << "is not recognized.";
}
}
checkRequirements();
Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last
setContextProperty( "Localeq", m_config );
}

View File

@ -0,0 +1,76 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019-2020 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 LOCALE_QMLVIEWSTEP_H
#define LOCALE_QMLVIEWSTEP_H
#include "Config.h"
#include "geoip/Handler.h"
#include "geoip/Interface.h"
#include "utils/PluginFactory.h"
#include "viewpages/QmlViewStep.h"
#include <DllMacro.h>
#include <QFutureWatcher>
#include <QObject>
#include <memory>
class PLUGINDLLEXPORT LocaleQmlViewStep : public Calamares::QmlViewStep
{
Q_OBJECT
public:
explicit LocaleQmlViewStep( QObject* parent = nullptr );
QString prettyName() const override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
Calamares::JobList jobs() const override;
void onActivate() override;
void onLeave() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
QObject* getConfig() override;
virtual Calamares::RequirementsList checkRequirements() override;
private:
// TODO: a generic QML viewstep should return a config object from a method
Config *m_config;
bool m_nextEnabled;
QString m_prettyStatus;
CalamaresUtils::GeoIP::RegionZonePair m_startingTimezone;
QString m_localeGenPath;
Calamares::JobList m_jobs;
std::unique_ptr< CalamaresUtils::GeoIP::Handler > m_geoip;
void fetchGeoIpTimezone();
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( LocaleQmlViewStepFactory )
#endif

View File

@ -0,0 +1,97 @@
---
# This settings are used to set your default system time zone.
# Time zones are usually located under /usr/share/zoneinfo and
# provided by the 'tzdata' package of your Distribution.
#
# Distributions using systemd can list available
# time zones by using the timedatectl command.
# timedatectl list-timezones
#
# The starting timezone (e.g. the pin-on-the-map) when entering
# the locale page can be set through keys *region* and *zone*.
# If either is not set, defaults to America/New_York.
#
region: "America"
zone: "New_York"
# System locales are detected in the following order:
#
# - /usr/share/i18n/SUPPORTED
# - localeGenPath (defaults to /etc/locale.gen if not set)
# - 'locale -a' output
#
# Enable only when your Distribution is using an
# custom path for locale.gen
#
#localeGenPath: "PATH_TO/locale.gen"
# GeoIP based Language settings: Leave commented out to disable GeoIP.
#
# GeoIP needs a working Internet connection.
# This can be managed from `welcome.conf` by adding
# internet to the list of required conditions.
#
# The configuration
# is in three parts: a *style*, which can be "json" or "xml"
# depending on the kind of data returned by the service, and
# a *url* where the data is retrieved, and an optional *selector*
# to pick the right field out of the returned data (e.g. field
# name in JSON or element name in XML).
#
# The default selector (when the setting is blank) is picked to
# work with existing JSON providers (which use "time_zone") and
# Ubiquity's XML providers (which use "TimeZone").
#
# If the service configured via *url* uses
# a different attribute name (e.g. "timezone") in JSON or a
# different element tag (e.g. "<Time_Zone>") in XML, set this
# string to the name or tag to be used.
#
# In JSON:
# - if the string contains "." characters, this is used as a
# multi-level selector, e.g. "a.b" will select the timezone
# from data "{a: {b: "Europe/Amsterdam" } }".
# - each part of the string split by "." characters is used as
# a key into the JSON data.
# In XML:
# - all elements with the named tag (e.g. all TimeZone) elements
# from the document are checked; the first one with non-empty
# text value is used.
#
#
# An HTTP(S) request is made to *url*. The request should return
# valid data in a suitable format, depending on *style*;
# generally this includes a string value with the timezone
# in <region>/<zone> format. For services that return data which
# does not follow the conventions of "suitable data" described
# below, *selector* may be used to pick different data.
#
# Note that this example URL works, but the service is shutting
# down in June 2018.
#
# Suitable JSON data looks like
# ```
# {"time_zone":"America/New_York"}
# ```
# Suitable XML data looks like
# ```
# <Response><TimeZone>Europe/Brussels</TimeZone></Response>
# ```
#
# To accommodate providers of GeoIP timezone data with peculiar timezone
# naming conventions, the following cleanups are performed automatically:
# - backslashes are removed
# - spaces are replaced with _
#
# Legacy settings "geoipStyle", "geoipUrl" and "geoipSelector"
# in the top-level are still supported, but I'd advise against.
#
# To disable GeoIP checking, either comment-out the entire geoip section,
# or set the *style* key to an unsupported format (e.g. `none`).
# Also, note the analogous feature in src/modules/welcome/welcome.conf.
#
geoip:
style: "json"
url: "https://geoip.kde.org/v1/calamares"
selector: "" # leave blank for the default

View File

@ -0,0 +1,113 @@
import io.calamares.modules 1.0 as Modules
import io.calamares.ui 1.0
import QtQuick 2.10
import QtQuick.Controls 2.10
import QtQuick.Layouts 1.3
import org.kde.kirigami 2.7 as Kirigami
import QtGraphicalEffects 1.0
ResponsiveBase
{
id: control
Modules.Locale //locale handler
{
id: _locale
}
title: stackView.currentItem.title
subtitle: stackView.currentItem.subtitle
message: stackView.currentItem.message
stackView.initialItem: Item
{
id: _regionsListComponent
property string title: qsTr("Region")
property string subtitle: qsTr("Pick your preferred region or use the default one based on your current location")
property string message: qsTr("Select your preferred zone within your location to continue with the installation")
ListViewTemplate
{
id: _regionListView
anchors.centerIn: parent
implicitWidth: Math.min(parent.width, 500)
implicitHeight: Math.min(contentHeight, 500)
currentIndex: model.currentIndex
model: _locale.Config.regionModel
delegate: ListItemDelegate
{
id: _delegate
label1.text: model.label
onClicked:
{
_regionListView.model.currentIndex = index
_stackView.push(_zonesListComponent)
}
}
footer: RowLayout
{
width: parent.width
z: 99999
Button
{
Layout.fillWidth: true
text: qsTr("Timezones")
icon.name: "go-previous"
onClicked: control.stackView.push(_zonesListComponent)
}
}
}
}
Component
{
id: _zonesListComponent
Item
{
property string title: qsTr("Timezone")
property string subtitle: _locale.Config.prettyStatus
property string message: ""
ListViewTemplate
{
id: _zonesListView
anchors.centerIn: parent
implicitWidth: Math.min(parent.width, 500)
implicitHeight: Math.min(contentHeight, 500)
currentIndex: model.currentIndex
model: _locale.Config.zonesModel
delegate: ListItemDelegate
{
id: _delegate
label1.text: model.label
onClicked:
{
_zonesListView.model.currentIndex = index
positionViewAtIndex(index, ListView.Center)
}
}
footer: RowLayout
{
width: parent.width
z: 99999
Button
{
Layout.fillWidth: true
icon.name: "go-previous"
text: qsTr("Regions")
onClicked: control.stackView.pop()
}
}
}
}
}
}

View File

@ -187,7 +187,7 @@ NetInstallPage::loadGroupList( const QString& confUrl )
using namespace CalamaresUtils::Network;
cDebug() << "NetInstall loading groups from" << confUrl;
QNetworkReply* reply = Manager::instance().asynchronouseGet(
QNetworkReply* reply = Manager::instance().asynchronousGet(
QUrl( confUrl ),
RequestOptions( RequestOptions::FakeUserAgent | RequestOptions::FollowRedirect, std::chrono::seconds( 30 ) ) );

View File

@ -2,8 +2,9 @@
*
* Copyright 2014, Aurélien Gâteau <agateau@kde.org>
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
* Copyright 2018-2019, 2020, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
* Copyright 2020, Anke Boersma <demm@kaosx.us
*
* 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,23 +23,22 @@
#include "gui/PartitionViewStep.h"
#include "core/DeviceModel.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h"
#include "core/PartitionModel.h"
#include "core/KPMHelpers.h"
#include "core/OsproberEntry.h"
#include "core/PartUtils.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h"
#include "core/PartitionModel.h"
#include "gui/ChoicePage.h"
#include "gui/PartitionPage.h"
#include "gui/PartitionBarsView.h"
#include "gui/PartitionLabelsView.h"
#include "gui/PartitionPage.h"
#include "Branding.h"
#include "CalamaresVersion.h"
#include "GlobalStorage.h"
#include "Job.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/NamedEnum.h"
@ -51,17 +51,16 @@
#include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystem.h>
// Qt
#include <QApplication>
#include <QDir>
#include <QFormLayout>
#include <QFutureWatcher>
#include <QLabel>
#include <QMessageBox>
#include <QProcess>
#include <QStackedWidget>
#include <QTimer>
#include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher>
PartitionViewStep::PartitionViewStep( QObject* parent )
: Calamares::ViewStep( parent )
@ -108,20 +107,22 @@ PartitionViewStep::continueLoading()
m_waitingWidget->deleteLater();
m_waitingWidget = nullptr;
connect( m_core, &PartitionCoreModule::hasRootMountPointChanged,
this, &PartitionViewStep::nextStatusChanged );
connect( m_choicePage, &ChoicePage::nextStatusChanged,
this, &PartitionViewStep::nextStatusChanged );
connect( m_core, &PartitionCoreModule::hasRootMountPointChanged, this, &PartitionViewStep::nextStatusChanged );
connect( m_choicePage, &ChoicePage::nextStatusChanged, this, &PartitionViewStep::nextStatusChanged );
}
PartitionViewStep::~PartitionViewStep()
{
if ( m_choicePage && m_choicePage->parent() == nullptr )
{
m_choicePage->deleteLater();
}
if ( m_manualPartitionPage && m_manualPartitionPage->parent() == nullptr )
{
m_manualPartitionPage->deleteLater();
}
}
QString
@ -168,12 +169,12 @@ PartitionViewStep::createSummaryWidget() const
.arg( *Calamares::Branding::ShortVersionedName );
break;
case ChoicePage::Erase:
modeText = tr( "<strong>Erase</strong> disk and install %1." )
.arg( *Calamares::Branding::ShortVersionedName );
modeText
= tr( "<strong>Erase</strong> disk and install %1." ).arg( *Calamares::Branding::ShortVersionedName );
break;
case ChoicePage::Replace:
modeText = tr( "<strong>Replace</strong> a partition with %1." )
.arg( *Calamares::Branding::ShortVersionedName );
modeText
= tr( "<strong>Replace</strong> a partition with %1." ).arg( *Calamares::Branding::ShortVersionedName );
break;
case ChoicePage::NoChoice:
case ChoicePage::Manual:
@ -190,7 +191,8 @@ PartitionViewStep::createSummaryWidget() const
switch ( choice )
{
case ChoicePage::Alongside:
modeText = tr( "Install %1 <strong>alongside</strong> another operating system on disk <strong>%2</strong> (%3)." )
modeText = tr( "Install %1 <strong>alongside</strong> another operating system on disk "
"<strong>%2</strong> (%3)." )
.arg( *Calamares::Branding::ShortVersionedName )
.arg( info.deviceNode )
.arg( info.deviceName );
@ -217,9 +219,8 @@ PartitionViewStep::createSummaryWidget() const
}
else // multiple disk previews!
{
diskInfoLabel->setText( tr( "Disk <strong>%1</strong> (%2)" )
.arg( info.deviceNode )
.arg( info.deviceName ) );
diskInfoLabel->setText(
tr( "Disk <strong>%1</strong> (%2)" ).arg( info.deviceNode ).arg( info.deviceName ) );
}
formLayout->addRow( diskInfoLabel );
@ -227,10 +228,10 @@ PartitionViewStep::createSummaryWidget() const
PartitionLabelsView* previewLabels;
QVBoxLayout* field;
PartitionBarsView::NestedPartitionsMode mode = Calamares::JobQueue::instance()->globalStorage()->
value( "drawNestedPartitions" ).toBool() ?
PartitionBarsView::DrawNestedPartitions :
PartitionBarsView::NoNestedPartitions;
PartitionBarsView::NestedPartitionsMode mode
= Calamares::JobQueue::instance()->globalStorage()->value( "drawNestedPartitions" ).toBool()
? PartitionBarsView::DrawNestedPartitions
: PartitionBarsView::NoNestedPartitions;
preview = new PartitionBarsView;
preview->setNestedPartitionsMode( mode );
previewLabels = new PartitionLabelsView;
@ -268,8 +269,10 @@ PartitionViewStep::createSummaryWidget() const
foreach ( const Calamares::job_ptr& job, jobs() )
{
if ( !job->prettyDescription().isEmpty() )
{
jobsLines.append( job->prettyDescription() );
}
}
if ( !jobsLines.isEmpty() )
{
QLabel* jobsLabel = new QLabel( widget );
@ -301,8 +304,10 @@ PartitionViewStep::next()
m_widget->setCurrentWidget( m_manualPartitionPage );
m_manualPartitionPage->selectDeviceByIndex( m_choicePage->lastSelectedDeviceIndex() );
if ( m_core->isDirty() )
{
m_manualPartitionPage->onRevertClicked();
}
}
cDebug() << "Choice applied: " << m_choicePage->currentChoice();
}
}
@ -329,10 +334,14 @@ bool
PartitionViewStep::isNextEnabled() const
{
if ( m_choicePage && m_widget->currentWidget() == m_choicePage )
{
return m_choicePage->isNextEnabled();
}
if ( m_manualPartitionPage && m_widget->currentWidget() == m_manualPartitionPage )
{
return m_core->hasRootMountPoint();
}
return false;
}
@ -349,7 +358,9 @@ bool
PartitionViewStep::isAtBeginning() const
{
if ( m_widget->currentWidget() != m_choicePage )
{
return false;
}
return true;
}
@ -359,10 +370,11 @@ PartitionViewStep::isAtEnd() const
{
if ( m_widget->currentWidget() == m_choicePage )
{
if ( m_choicePage->currentChoice() == ChoicePage::Erase ||
m_choicePage->currentChoice() == ChoicePage::Replace ||
m_choicePage->currentChoice() == ChoicePage::Alongside )
if ( m_choicePage->currentChoice() == ChoicePage::Erase || m_choicePage->currentChoice() == ChoicePage::Replace
|| m_choicePage->currentChoice() == ChoicePage::Alongside )
{
return true;
}
return false;
}
return true;
@ -381,8 +393,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 )
if ( m_widget->currentWidget() == m_choicePage && m_choicePage->currentChoice() == ChoicePage::Alongside )
{
m_choicePage->applyActionChoice( ChoicePage::Alongside );
// m_choicePage->reset();
@ -404,8 +415,8 @@ PartitionViewStep::onLeave()
{
if ( PartUtils::isEfiSystem() )
{
QString espMountPoint = Calamares::JobQueue::instance()->globalStorage()->
value( "efiSystemPartition" ).toString();
QString espMountPoint
= Calamares::JobQueue::instance()->globalStorage()->value( "efiSystemPartition" ).toString();
Partition* esp = m_core->findPartitionByMountPoint( espMountPoint );
QString message;
@ -443,11 +454,32 @@ PartitionViewStep::onLeave()
if ( !message.isEmpty() )
{
cWarning() << message;
QMessageBox::warning( m_manualPartitionPage,
message,
description );
QMessageBox::warning( m_manualPartitionPage, message, description );
}
}
else
{
cDebug() << "device: BIOS";
// TODO: this *always* warns, which might be annoying, so it'd be
// best to find a way to detect that bios_grub partition.
QString message = tr( "Option to use GPT on BIOS" );
QString description = tr( "A GPT partition table is the best option for all "
"systems. This installer supports such a setup for "
"BIOS systems too."
"<br/><br/>"
"To configure a GPT partition table on BIOS, "
"(if not done so already) go back "
"and set the partion table to GPT, next create a 8 MB "
"unformatted partition with the "
"<strong>bios_grub</strong> flag enabled.<br/><br/>"
"An unformatted 8 MB partition is necessary "
"to start %1 on a BIOS system with GPT." )
.arg( *Calamares::Branding::ShortProductName );
QMessageBox::information( m_manualPartitionPage, message, description );
}
Partition* root_p = m_core->findPartitionByMountPoint( "/" );
Partition* boot_p = m_core->findPartitionByMountPoint( "/boot" );
@ -459,8 +491,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 )
if ( root_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 "
@ -476,9 +507,7 @@ PartitionViewStep::onLeave()
"recreate it, selecting <strong>Encrypt</strong> "
"in the partition creation window." );
QMessageBox::warning( m_manualPartitionPage,
message,
description );
QMessageBox::warning( m_manualPartitionPage, message, description );
}
}
}
@ -493,7 +522,9 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
QString efiSP = CalamaresUtils::getString( configurationMap, "efiSystemPartition" );
if ( efiSP.isEmpty() )
{
efiSP = QStringLiteral( "/boot/efi" );
}
gs->insert( "efiSystemPartition", efiSP );
// Set up firmwareType global storage entry. This is used, e.g. by the bootloader module.
@ -511,16 +542,22 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
//
// This is a bit convoluted because there's legacy settings to handle as well
// as the new-style list of choices, with mapping back-and-forth.
if ( configurationMap.contains( "userSwapChoices" ) &&
( configurationMap.contains( "ensureSuspendToDisk" ) || configurationMap.contains( "neverCreateSwap" ) ) )
if ( configurationMap.contains( "userSwapChoices" )
&& ( configurationMap.contains( "ensureSuspendToDisk" ) || configurationMap.contains( "neverCreateSwap" ) ) )
{
cError() << "Partition-module configuration mixes old- and new-style swap settings.";
}
if ( configurationMap.contains( "ensureSuspendToDisk" ) )
{
cWarning() << "Partition-module setting *ensureSuspendToDisk* is deprecated.";
}
bool ensureSuspendToDisk = CalamaresUtils::getBool( configurationMap, "ensureSuspendToDisk", true );
if ( configurationMap.contains( "neverCreateSwap" ) )
{
cWarning() << "Partition-module setting *neverCreateSwap* is deprecated.";
}
bool neverCreateSwap = CalamaresUtils::getBool( configurationMap, "neverCreateSwap", false );
QSet< PartitionActions::Choices::SwapChoice > choices; // Available swap choices
@ -535,8 +572,10 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
bool ok = false;
auto v = PartitionActions::Choices::nameToChoice( item, ok );
if ( ok )
{
choices.insert( v );
}
}
if ( choices.isEmpty() )
{
@ -553,19 +592,28 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
// Convert the legacy settings into a single setting for now.
if ( neverCreateSwap )
{
choices.insert( PartitionActions::Choices::SwapChoice::NoSwap );
}
else if ( ensureSuspendToDisk )
{
choices.insert( PartitionActions::Choices::SwapChoice::FullSwap );
}
else
{
choices.insert( PartitionActions::Choices::SwapChoice::SmallSwap );
}
}
// Not all are supported right now // FIXME
static const char unsupportedSetting[] = "Partition-module does not support *userSwapChoices* setting";
#define COMPLAIN_UNSUPPORTED( x ) \
if ( choices.contains( x ) ) \
{ cWarning() << unsupportedSetting << PartitionActions::Choices::choiceToName( x ); choices.remove( x ); }
{ \
cWarning() << unsupportedSetting << PartitionActions::Choices::choiceToName( x ); \
choices.remove( x ); \
}
COMPLAIN_UNSUPPORTED( PartitionActions::Choices::SwapChoice::SwapFile )
COMPLAIN_UNSUPPORTED( PartitionActions::Choices::SwapChoice::ReuseSwap )
@ -584,23 +632,35 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
// OTHER SETTINGS
//
gs->insert( "drawNestedPartitions", CalamaresUtils::getBool( configurationMap, "drawNestedPartitions", false ) );
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( "alwaysShowPartitionLabels",
CalamaresUtils::getBool( configurationMap, "alwaysShowPartitionLabels", true ) );
gs->insert( "enableLuksAutomatedPartitioning",
CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) );
gs->insert( "allowManualPartitioning",
CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ) );
// 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 (" << fsName << ") using" << fsRealName << "instead.";
{
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << fsName << ") using" << fsRealName
<< "instead.";
}
gs->insert( "defaultFileSystemType", fsRealName );
@ -608,16 +668,13 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
// because it could take a while. Then when it's done, we can set up the widgets
// and remove the spinner.
m_future = new QFutureWatcher< void >();
connect( m_future, &QFutureWatcher< void >::finished,
this, [ this ]
{
connect( m_future, &QFutureWatcher< void >::finished, this, [this] {
continueLoading();
this->m_future->deleteLater();
this->m_future = nullptr;
} );
QFuture< void > future =
QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
QFuture< void > future = QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
m_future->setFuture( future );
if ( configurationMap.contains( "partitionLayout" ) )
@ -641,11 +698,12 @@ Calamares::RequirementsList
PartitionViewStep::checkRequirements()
{
if ( m_future )
{
m_future->waitForFinished();
}
Calamares::RequirementsList l;
l.append(
{
l.append( {
QLatin1String( "partitions" ),
[] { return tr( "has at least one disk device available." ); },
[] { return tr( "There are no partitions to install on." ); },

View File

@ -12,8 +12,6 @@ else()
add_definitions( -DWITHOUT_LIBPARTED )
endif()
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
set( CHECKER_SOURCES
checker/CheckerContainer.cpp
checker/GeneralRequirements.cpp

View File

@ -17,25 +17,25 @@
*/
#include "Config.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include "Branding.h"
#include "Settings.h"
#include <QApplication>
#include "utils/Logger.h"
#include "utils/Retranslator.h"
void
RequirementsModel::setRequirementsList( const Calamares::RequirementsList& requirements )
{
CALAMARES_RETRANSLATE_SLOT( &RequirementsModel::retranslate )
emit beginResetModel();
m_requierements = requirements;
m_satisfiedRequirements = true;
m_requirements = requirements;
auto isUnSatisfied = []( const Calamares::RequirementEntry& e ) { return !e.satisfied; };
auto isMandatoryAndUnSatisfied = []( const Calamares::RequirementEntry& e ) { return e.mandatory && !e.satisfied; };
m_satisfiedRequirements = std::none_of( m_requierements.begin(), m_requierements.end(), isUnSatisfied );
m_satisfiedMandatory = std::none_of( m_requierements.begin(), m_requierements.end(), isMandatoryAndUnSatisfied );
m_satisfiedRequirements = std::none_of( m_requirements.begin(), m_requirements.end(), isUnSatisfied );
m_satisfiedMandatory = std::none_of( m_requirements.begin(), m_requirements.end(), isMandatoryAndUnSatisfied );
emit satisfiedRequirementsChanged( m_satisfiedRequirements );
emit satisfiedMandatoryChanged();
@ -45,13 +45,13 @@ RequirementsModel::setRequirementsList( const Calamares::RequirementsList& requi
int
RequirementsModel::rowCount( const QModelIndex& ) const
{
return m_requierements.count();
return m_requirements.count();
}
QVariant
RequirementsModel::data( const QModelIndex& index, int role ) const
{
const auto requirement = m_requierements.at( index.row() );
const auto requirement = m_requirements.at( index.row() );
switch ( role )
{
@ -82,7 +82,8 @@ RequirementsModel::roleNames() const
return roles;
}
Config::Config( QObject* parent ) : QObject( parent )
Config::Config( QObject* parent )
: QObject( parent )
, m_requirementsModel( new RequirementsModel( this ) )
, m_languages( CalamaresUtils::Locale::availableTranslations() )
{
@ -91,31 +92,15 @@ Config::Config( QObject* parent ) : QObject( parent )
initLanguages();
CALAMARES_RETRANSLATE_SLOT( &Config::retranslate )
}
void
Config::retranslate()
{
QString message;
if ( Calamares::Settings::instance()->isSetupMode() )
{
message = Calamares::Branding::instance()->welcomeStyleCalamares()
? tr( "<h1>Welcome to the Calamares setup program for %1.</h1>" )
: tr( "<h1>Welcome to %1 setup.</h1>" );
}
else
{
message = Calamares::Branding::instance()->welcomeStyleCalamares()
? tr( "<h1>Welcome to the Calamares installer for %1.</h1>" )
: tr( "<h1>Welcome to the %1 installer.</h1>" );
}
m_genericWelcomeMessage = message.arg( *Calamares::Branding::VersionedName );
m_genericWelcomeMessage = genericWelcomeMessage().arg( *Calamares::Branding::VersionedName );
emit genericWelcomeMessageChanged();
// ui->supportButton->setText( tr( "%1 support" ).arg( *Calamares::Branding::ShortProductName ) );
m_requirementsModel->retranslate();
}
CalamaresUtils::Locale::LabelModel*
@ -168,7 +153,7 @@ Config::initLanguages()
QString name = m_languages->locale( matchedLocaleIndex ).name();
cDebug() << Logger::SubEntry << "Matched with index" << matchedLocaleIndex << name;
CalamaresUtils::installTranslator( name, Calamares::Branding::instance()->translationsDirectory(), qApp );
CalamaresUtils::installTranslator( name, Calamares::Branding::instance()->translationsDirectory() );
setLocaleIndex( matchedLocaleIndex );
}
else
@ -195,8 +180,11 @@ Config::setLanguageIcon(const QString &languageIcon )
void
Config::setLocaleIndex( const int& index )
{
if(index == m_localeIndex || index > CalamaresUtils::Locale::availableTranslations()->rowCount(QModelIndex()) || index < 0)
if ( index == m_localeIndex || index > CalamaresUtils::Locale::availableTranslations()->rowCount( QModelIndex() )
|| index < 0 )
{
return;
}
m_localeIndex = index;
@ -204,8 +192,7 @@ Config::setLocaleIndex(const int& index)
cDebug() << "Selected locale" << selectedLocale;
QLocale::setDefault( selectedLocale );
CalamaresUtils::installTranslator(
selectedLocale, Calamares::Branding::instance()->translationsDirectory(), qApp );
CalamaresUtils::installTranslator( selectedLocale, Calamares::Branding::instance()->translationsDirectory() );
emit localeIndexChanged( m_localeIndex );
}
@ -223,51 +210,118 @@ Config::setIsNextEnabled( const bool& isNextEnabled )
emit isNextEnabledChanged( m_isNextEnabled );
}
QString Config::donateUrl() const
QString
Config::donateUrl() const
{
return m_donateUrl;
}
void Config::setDonateUrl(const QString& url)
void
Config::setDonateUrl( const QString& url )
{
m_donateUrl = url;
emit donateUrlChanged();
}
QString Config::knownIssuesUrl() const
QString
Config::knownIssuesUrl() const
{
return m_knownIssuesUrl;
}
void Config::setKnownIssuesUrl(const QString& url)
void
Config::setKnownIssuesUrl( const QString& url )
{
m_knownIssuesUrl = url;
emit knownIssuesUrlChanged();
}
void Config::setReleaseNotesUrl(const QString& url)
void
Config::setReleaseNotesUrl( const QString& url )
{
m_releaseNotesUrl = url;
emit releaseNotesUrlChanged();
}
QString Config::releaseNotesUrl() const
QString
Config::releaseNotesUrl() const
{
return m_releaseNotesUrl;
}
QString Config::supportUrl() const
QString
Config::supportUrl() const
{
return m_supportUrl;
}
void Config::setSupportUrl(const QString& url)
void
Config::setSupportUrl( const QString& url )
{
m_supportUrl = url;
emit supportUrlChanged();
}
void
RequirementsModel::retranslate()
{
if ( !m_satisfiedRequirements )
{
QString message;
const bool setup = Calamares::Settings::instance()->isSetupMode();
if ( !m_satisfiedMandatory )
{
message = setup ? tr( "This computer does not satisfy the minimum "
"requirements for setting up %1.<br/>"
"Setup cannot continue. "
"<a href=\"#details\">Details...</a>" )
: tr( "This computer does not satisfy the minimum "
"requirements for installing %1.<br/>"
"Installation cannot continue. "
"<a href=\"#details\">Details...</a>" );
}
else
{
message = setup ? tr( "This computer does not satisfy some of the "
"recommended requirements for setting up %1.<br/>"
"Setup can continue, but some features "
"might be disabled." )
: tr( "This computer does not satisfy some of the "
"recommended requirements for installing %1.<br/>"
"Installation can continue, but some features "
"might be disabled." );
}
m_warningMessage = message.arg( *Calamares::Branding::ShortVersionedName );
}
else
{
m_warningMessage = tr( "This program will ask you some questions and "
"set up %2 on your computer." )
.arg( *Calamares::Branding::ProductName );
}
emit warningMessageChanged();
}
QString
Config::genericWelcomeMessage()
{
QString message;
if ( Calamares::Settings::instance()->isSetupMode() )
{
message = Calamares::Branding::instance()->welcomeStyleCalamares()
? tr( "<h1>Welcome to the Calamares setup program for %1.</h1>" )
: tr( "<h1>Welcome to %1 setup.</h1>" );
}
else
{
message = Calamares::Branding::instance()->welcomeStyleCalamares()
? tr( "<h1>Welcome to the Calamares installer for %1.</h1>" )
: tr( "<h1>Welcome to the %1 installer.</h1>" );
}
return message;
}

View File

@ -19,21 +19,23 @@
#ifndef WELCOME_CONFIG_H
#define WELCOME_CONFIG_H
#include <QObject>
#include <QUrl>
#include "modulesystem/Requirement.h"
#include "locale/LabelModel.h"
#include <QObject>
#include <QUrl>
// TODO: move this (and modulesystem/Requirement) to libcalamares
class RequirementsModel : public QAbstractListModel
{
Q_OBJECT
using QAbstractListModel::QAbstractListModel;
Q_PROPERTY( bool satisfiedRequirements READ satisfiedRequirements NOTIFY satisfiedRequirementsChanged FINAL )
Q_PROPERTY( bool satisfiedMandatory READ satisfiedMandatory NOTIFY satisfiedMandatoryChanged FINAL )
Q_PROPERTY( QString warningMessage READ warningMessage NOTIFY warningMessageChanged FINAL )
public:
using QAbstractListModel::QAbstractListModel;
enum Roles : short
{
Name,
@ -44,32 +46,27 @@ public:
HasDetails
};
bool satisfiedRequirements() const
{
return m_satisfiedRequirements;
}
bool satisfiedRequirements() const { return m_satisfiedRequirements; }
bool satisfiedMandatory() const
{
return m_satisfiedMandatory;
}
bool satisfiedMandatory() const { return m_satisfiedMandatory; }
const Calamares::RequirementEntry& getEntry( const int& index ) const
{
if ( index > count() || index < 0 )
{
return *( new Calamares::RequirementEntry() );
}
return m_requierements.at(index);
return m_requirements.at( index );
}
void setRequirementsList( const Calamares::RequirementsList& requirements );
int rowCount( const QModelIndex& ) const override;
int count() const
{
return m_requierements.count();
}
int count() const { return m_requirements.count(); }
QString warningMessage() const { return m_warningMessage; }
void retranslate();
QVariant data( const QModelIndex& index, int role ) const override;
@ -77,13 +74,16 @@ protected:
QHash< int, QByteArray > roleNames() const override;
private:
Calamares::RequirementsList m_requierements;
Calamares::RequirementsList m_requirements;
bool m_satisfiedRequirements = false;
bool m_satisfiedMandatory = false;
QString m_warningMessage;
signals:
void satisfiedRequirementsChanged( bool value );
void satisfiedMandatoryChanged();
void warningMessageChanged();
};
@ -99,12 +99,13 @@ class Config : public QObject
Q_PROPERTY( int localeIndex READ localeIndex WRITE setLocaleIndex NOTIFY localeIndexChanged )
Q_PROPERTY( QString genericWelcomeMessage MEMBER m_genericWelcomeMessage NOTIFY genericWelcomeMessageChanged FINAL )
Q_PROPERTY( QString warningMessage MEMBER m_warningMessage CONSTANT FINAL )
Q_PROPERTY(QString supportUrl MEMBER m_supportUrl CONSTANT FINAL)
Q_PROPERTY(QString knownIssuesUrl MEMBER m_knownIssuesUrl CONSTANT FINAL)
Q_PROPERTY(QString releaseNotesUrl MEMBER m_releaseNotesUrl CONSTANT FINAL)
Q_PROPERTY(QString donateUrl MEMBER m_donateUrl CONSTANT FINAL)
Q_PROPERTY( QString supportUrl MEMBER m_supportUrl NOTIFY supportUrlChanged FINAL )
Q_PROPERTY( QString knownIssuesUrl MEMBER m_knownIssuesUrl NOTIFY knownIssuesUrlChanged FINAL )
Q_PROPERTY( QString releaseNotesUrl MEMBER m_releaseNotesUrl NOTIFY releaseNotesUrlChanged FINAL )
Q_PROPERTY( QString donateUrl MEMBER m_donateUrl NOTIFY donateUrlChanged FINAL )
Q_PROPERTY( bool isNextEnabled MEMBER m_isNextEnabled NOTIFY isNextEnabledChanged FINAL )
public:
Config( QObject* parent = nullptr );
@ -129,6 +130,9 @@ public:
QString donateUrl() const;
void setDonateUrl( const QString& url );
QString genericWelcomeMessage();
public slots:
CalamaresUtils::Locale::LabelModel* languagesModel() const;
void retranslate();
@ -144,9 +148,7 @@ private:
bool m_isNextEnabled = false;
CalamaresUtils::Locale::LabelModel* m_languages;
QString m_genericWelcomeMessage = tr("This program will ask you some questions and set up your installation");
QString m_warningMessage = tr("This program does not satisfy the minimum requirements for installing.\nInstallation can not continue");
QString m_genericWelcomeMessage;
QString m_supportUrl;
QString m_knownIssuesUrl;
@ -158,6 +160,10 @@ signals:
void localeIndexChanged( int localeIndex );
void isNextEnabledChanged( bool isNextEnabled );
void genericWelcomeMessageChanged();
void supportUrlChanged();
void knownIssuesUrlChanged();
void releaseNotesUrlChanged();
void donateUrlChanged();
};
#endif

View File

@ -25,9 +25,9 @@
#include "Branding.h"
#include "CalamaresVersion.h"
#include "Config.h"
#include "Settings.h"
#include "ViewManager.h"
#include "Config.h"
#include "locale/LabelModel.h"
#include "modulesystem/ModuleManager.h"
@ -84,7 +84,8 @@ WelcomePage::WelcomePage( Config *conf, QWidget* parent )
ui->verticalLayout->insertWidget( welcome_text_idx + 1, m_checkingWidget );
}
void WelcomePage::init()
void
WelcomePage::init()
{
//setup the url buttons
setupButton( WelcomePage::Button::Support, m_conf->supportUrl() );
@ -113,7 +114,9 @@ WelcomePage::initLanguages()
ui->languageWidget->setCurrentIndex( m_conf->localeIndex() );
connect( ui->languageWidget,
static_cast< void ( QComboBox::* )( int ) >( &QComboBox::currentIndexChanged ), m_conf, &Config::setLocaleIndex );
static_cast< void ( QComboBox::* )( int ) >( &QComboBox::currentIndexChanged ),
m_conf,
&Config::setLocaleIndex );
}
void
@ -232,7 +235,7 @@ WelcomePage::showAboutBox()
"<strong>%2<br/>"
"for %3</strong><br/><br/>"
"Copyright 2014-2017 Teo Mrnjavac &lt;teo@kde.org&gt;<br/>"
"Copyright 2017-2019 Adriaan de Groot &lt;groot@kde.org&gt;<br/>"
"Copyright 2017-2020 Adriaan de Groot &lt;groot@kde.org&gt;<br/>"
"Thanks to <a href=\"https://calamares.io/team/\">the Calamares team</a> "
"and the <a href=\"https://www.transifex.com/calamares/calamares/\">Calamares "
"translators team</a>.<br/><br/>"

View File

@ -18,19 +18,18 @@
*/
#include "WelcomeViewStep.h"
#include "Config.h"
#include "Config.h"
#include "WelcomePage.h"
#include "checker/GeneralRequirements.h"
#include "Branding.h"
#include "geoip/Handler.h"
#include "locale/Lookup.h"
#include "modulesystem/ModuleManager.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include "Branding.h"
#include "modulesystem/ModuleManager.h"
#include <QFutureWatcher>
#include <QVariant>
@ -145,8 +144,10 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap )
using Calamares::Branding;
m_conf->setSupportUrl( jobOrBrandingSetting( Branding::SupportUrl, configurationMap, "showSupportUrl" ) );
m_conf->setKnownIssuesUrl( jobOrBrandingSetting( Branding::KnownIssuesUrl, configurationMap, "showKnownIssuesUrl" ) );
m_conf->setReleaseNotesUrl( jobOrBrandingSetting( Branding::ReleaseNotesUrl, configurationMap, "showReleaseNotesUrl" ) );
m_conf->setKnownIssuesUrl(
jobOrBrandingSetting( Branding::KnownIssuesUrl, configurationMap, "showKnownIssuesUrl" ) );
m_conf->setReleaseNotesUrl(
jobOrBrandingSetting( Branding::ReleaseNotesUrl, configurationMap, "showReleaseNotesUrl" ) );
m_conf->setDonateUrl( CalamaresUtils::getString( configurationMap, "showDonateUrl" ) );
if ( configurationMap.contains( "requirements" )

View File

@ -16,22 +16,22 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WELCOMEPAGEPLUGIN_H
#define WELCOMEPAGEPLUGIN_H
#ifndef WELCOMEVIEWSTEP_H
#define WELCOMEVIEWSTEP_H
#include "DllMacro.h"
#include "modulesystem/Requirement.h"
#include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h"
#include <QObject>
#include <modulesystem/Requirement.h>
#include <utils/PluginFactory.h>
#include <viewpages/ViewStep.h>
#include <DllMacro.h>
#include <QVariantMap>
class WelcomePage;
class GeneralRequirements;
class Config;
namespace CalamaresUtils
{
namespace GeoIP
@ -80,4 +80,4 @@ private:
CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeViewStepFactory )
#endif // WELCOMEPAGEPLUGIN_H
#endif // WELCOMEVIEWSTEP_H

View File

@ -288,4 +288,6 @@ ResultsListWidget::retranslate()
}
}
#include "utils/moc-warnings.h"
#include "ResultsListWidget.moc"

View File

@ -41,7 +41,7 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeQmlViewStepFactory, registerPlugin<
WelcomeQmlViewStep::WelcomeQmlViewStep( QObject* parent )
: Calamares::QmlViewStep(parent )
, m_config( new Config( ) ) // the qml singleton takes ownership and deletes it
, m_config( new Config( this ) ) // the qml singleton takes ownership and deletes it
// , m_nextEnabled( false )
, m_requirementsChecker( new GeneralRequirements( this ) )
@ -98,10 +98,46 @@ WelcomeQmlViewStep::jobs() const
return Calamares::JobList();
}
/** @brief Look up a URL for a button
*
* Looks up @p key in @p map; if it is a *boolean* value, then
* assume an old-style configuration, and fetch the string from
* the branding settings @p e. If it is a string, not a boolean,
* use it as-is. If not found, or a weird type, returns empty.
*
* This allows switching the showKnownIssuesUrl and similar settings
* in welcome.conf from a boolean (deferring to branding) to an
* actual string for immediate use. Empty strings, as well as
* "false" as a setting, will hide the buttons as before.
*/
static QString
jobOrBrandingSetting( Calamares::Branding::StringEntry e, const QVariantMap& map, const QString& key )
{
if ( !map.contains( key ) )
{
return QString();
}
auto v = map.value( key );
if ( v.type() == QVariant::Bool )
{
return v.toBool() ? ( *e ) : QString();
}
if ( v.type() == QVariant::String )
{
return v.toString();
}
return QString();
}
void
WelcomeQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
using Calamares::Branding;
m_config->setSupportUrl( jobOrBrandingSetting( Branding::SupportUrl, configurationMap, "showSupportUrl" ) );
m_config->setKnownIssuesUrl( jobOrBrandingSetting( Branding::KnownIssuesUrl, configurationMap, "showKnownIssuesUrl" ) );
m_config->setReleaseNotesUrl( jobOrBrandingSetting( Branding::ReleaseNotesUrl, configurationMap, "showReleaseNotesUrl" ) );
m_config->setDonateUrl( CalamaresUtils::getString( configurationMap, "showDonateUrl" ) );
// TODO: expand Config class and set the remaining fields // with the configurationMap all those properties can be accesed withouth having to declare a property, get and setter for each