diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f31f0f36..7473876ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,7 +199,7 @@ set( CALAMARES_TRANSLATION_LANGUAGES ar ast bg ca cs_CZ da de el en en_GB es_MX set( CALAMARES_VERSION_MAJOR 3 ) set( CALAMARES_VERSION_MINOR 2 ) set( CALAMARES_VERSION_PATCH 0 ) -set( CALAMARES_VERSION_RC 1 ) +set( CALAMARES_VERSION_RC 2 ) set( CALAMARES_VERSION ${CALAMARES_VERSION_MAJOR}.${CALAMARES_VERSION_MINOR}.${CALAMARES_VERSION_PATCH} ) set( CALAMARES_VERSION_SHORT "${CALAMARES_VERSION}" ) diff --git a/src/modules/hwclock/main.py b/src/modules/hwclock/main.py index 8b31080dd..dd408a372 100644 --- a/src/modules/hwclock/main.py +++ b/src/modules/hwclock/main.py @@ -5,7 +5,8 @@ # # Copyright 2014 - 2015, Philip Müller # Copyright 2014, Teo Mrnjavac -# Copyright 2017. Alf Gaida +# Copyright 2017, Alf Gaida +# Copyright 2017, Gabriel Craciunescu # # Calamares is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,26 +21,33 @@ # You should have received a copy of the GNU General Public License # along with Calamares. If not, see . -import subprocess -import shutil - import libcalamares - def run(): """ Set hardware clock. """ + hwclock_rtc = ["hwclock", "--systohc", "--utc"] + hwclock_isa = ["hwclock", "--systohc", "--utc", "--directisa"] + is_broken_rtc = False + is_broken_isa = False - root_mount_point = libcalamares.globalstorage.value("rootMountPoint") - try: - subprocess.check_call(["hwclock", "--systohc", "--utc"]) - except subprocess.CalledProcessError as e: - return ( - "Cannot set hardware clock.", - "hwclock terminated with exit code {}.".format(e.returncode) - ) - - shutil.copy2("/etc/adjtime", "{!s}/etc/".format(root_mount_point)) + ret = libcalamares.utils.target_env_call(hwclock_rtc) + if ret != 0: + is_broken_rtc = True + libcalamares.utils.debug("Hwclock returned error code {}".format(ret)) + libcalamares.utils.debug(" .. RTC method failed, trying ISA bus method.") + else: + libcalamares.utils.debug("Hwclock set using RTC method.") + if is_broken_rtc: + ret = libcalamares.utils.target_env_call(hwclock_isa) + if ret != 0: + is_broken_isa = True + libcalamares.utils.debug("Hwclock returned error code {}".format(ret)) + libcalamares.utils.debug(" .. ISA bus method failed.") + else: + libcalamares.utils.debug("Hwclock set using ISA bus methode.") + if is_broken_rtc and is_broken_isa: + libcalamares.utils.debug("BIOS or Kernel BUG: Setting hwclock failed.") return None diff --git a/src/modules/plasmalnf/CMakeLists.txt b/src/modules/plasmalnf/CMakeLists.txt index 61b44862f..8148a80e9 100644 --- a/src/modules/plasmalnf/CMakeLists.txt +++ b/src/modules/plasmalnf/CMakeLists.txt @@ -9,6 +9,9 @@ calamares_add_plugin( plasmalnf PlasmaLnfViewStep.cpp PlasmaLnfPage.cpp PlasmaLnfJob.cpp + ThemeWidget.cpp + RESOURCES + page_plasmalnf.qrc UI page_plasmalnf.ui LINK_PRIVATE_LIBRARIES diff --git a/src/modules/plasmalnf/PlasmaLnfPage.cpp b/src/modules/plasmalnf/PlasmaLnfPage.cpp index 01759ba32..2b171cc40 100644 --- a/src/modules/plasmalnf/PlasmaLnfPage.cpp +++ b/src/modules/plasmalnf/PlasmaLnfPage.cpp @@ -26,19 +26,26 @@ #include #include -static PlasmaLnfList plasma_themes() +ThemeInfo::ThemeInfo( const KPluginMetaData& data ) + : id( data.pluginId() ) + , name( data.name() ) + , description( data.description() ) + , widget( nullptr ) { - PlasmaLnfList packages; +} + +static ThemeInfoList plasma_themes() +{ + ThemeInfoList packages; QList pkgs = KPackage::PackageLoader::self()->listPackages( "Plasma/LookAndFeel" ); for ( const KPluginMetaData& data : pkgs ) { - packages << PlasmaLnfDescriptor{ data.pluginId(), data.name() }; - cDebug() << "LNF Package" << data.pluginId(); - cDebug() << " .." << data.name(); - cDebug() << " .." << data.description(); - cDebug() << " .." << 'V' << data.isValid() << 'H' << data.isHidden() << 'D' << data.isEnabledByDefault(); + if ( data.isValid() && !data.isHidden() && !data.name().isEmpty() ) + { + packages << ThemeInfo{ data }; + } } return packages; @@ -48,38 +55,113 @@ static PlasmaLnfList plasma_themes() PlasmaLnfPage::PlasmaLnfPage( QWidget* parent ) : QWidget( parent ) , ui( new Ui::PlasmaLnfPage ) + , m_buttonGroup( nullptr ) { ui->setupUi( this ); CALAMARES_RETRANSLATE( { ui->retranslateUi( this ); - ui->generalExplanation->setText( tr( "Please choose a look-and-feel for the KDE Plasma Desktop, below." ) ); - m_availableLnf = plasma_themes(); - ui->lnfCombo->clear(); - for ( const auto& p : m_availableLnf ) - ui->lnfCombo->addItem( p.name ); + ui->generalExplanation->setText( tr( "Please choose a look-and-feel for the KDE Plasma Desktop. You can also skip this step and configure the look-and-feel once the system is installed." ) ); + updateThemeNames(); + fillUi(); } ) +} + +void +PlasmaLnfPage::setLnfPath( const QString& path ) +{ + m_lnfPath = path; +} - QObject::connect( ui->lnfCombo, &QComboBox::activated, this, &PlasmaLnfPage::activated ); +void +PlasmaLnfPage::setEnabledThemes(const ThemeInfoList& themes) +{ + m_enabledThemes = themes; + + updateThemeNames(); + winnowThemes(); + fillUi(); } void -PlasmaLnfPage::activated( int index ) +PlasmaLnfPage::setEnabledThemesAll() { - if ( ( index < 0 ) || ( index > m_availableLnf.length() ) ) + setEnabledThemes( plasma_themes() ); +} + + +void PlasmaLnfPage::updateThemeNames() +{ + auto plasmaThemes = plasma_themes(); + for ( auto& enabled_theme : m_enabledThemes ) { - cDebug() << "Plasma LNF index" << index << "out of range."; - return; + ThemeInfo* t = plasmaThemes.findById( enabled_theme.id ); + if ( t != nullptr ) + { + enabled_theme.name = t->name; + enabled_theme.description = t->description; + } } +} + +void PlasmaLnfPage::winnowThemes() +{ + auto plasmaThemes = plasma_themes(); + bool winnowed = true; + int winnow_index = 0; + while ( winnowed ) + { + winnowed = false; + winnow_index = 0; + + for ( auto& enabled_theme : m_enabledThemes ) + { + ThemeInfo* t = plasmaThemes.findById( enabled_theme.id ); + if ( t == nullptr ) + { + cDebug() << "Removing" << enabled_theme.id; + winnowed = true; + break; + } + ++winnow_index; + } - const PlasmaLnfDescriptor& lnf = m_availableLnf.at( index ); - cDebug() << "Changed to" << index << lnf.id << lnf.name; - emit plasmaThemeSelected( lnf.id ); + if ( winnowed ) + { + m_enabledThemes.removeAt( winnow_index ); + } + } } -void -PlasmaLnfPage::setLnfPath( const QString& path ) +void PlasmaLnfPage::fillUi() { - m_lnfPath = path; + if ( m_enabledThemes.isEmpty() ) + { + return; + } + + if ( !m_buttonGroup ) + { + m_buttonGroup = new QButtonGroup( this ); + m_buttonGroup->setExclusive( true ); + } + + int c = 1; // After the general explanation + for ( auto& theme : m_enabledThemes ) + { + if ( !theme.widget ) + { + ThemeWidget* w = new ThemeWidget( theme ); + m_buttonGroup->addButton( w->button() ); + ui->verticalLayout->insertWidget( c, w ); + connect( w, &ThemeWidget::themeSelected, this, &PlasmaLnfPage::plasmaThemeSelected); + theme.widget = w; + } + else + { + theme.widget->updateThemeName( theme ); + } + ++c; + } } diff --git a/src/modules/plasmalnf/PlasmaLnfPage.h b/src/modules/plasmalnf/PlasmaLnfPage.h index 31731eb0d..e489e99a7 100644 --- a/src/modules/plasmalnf/PlasmaLnfPage.h +++ b/src/modules/plasmalnf/PlasmaLnfPage.h @@ -19,23 +19,26 @@ #ifndef PLASMALNFPAGE_H #define PLASMALNFPAGE_H +#include #include #include +#include #include +#include "ThemeInfo.h" +#include "ThemeWidget.h" + namespace Ui { class PlasmaLnfPage; } -struct PlasmaLnfDescriptor -{ - QString id; - QString name; -} ; - -using PlasmaLnfList = QList; - +/** @brief Page for selecting a Plasma Look-and-Feel theme. + * + * You must call setEnabledThemes -- either overload -- once + * to get the selection widgets. Note that calling that with + * an empty list will result in zero (0) selectable themes. + */ class PlasmaLnfPage : public QWidget { Q_OBJECT @@ -43,17 +46,28 @@ public: explicit PlasmaLnfPage( QWidget* parent = nullptr ); void setLnfPath( const QString& path ); - -public slots: - void activated( int index ); + /** @brief enable only the listed themes. */ + void setEnabledThemes( const ThemeInfoList& themes ); + /** @brief enable all installed plasma themes. */ + void setEnabledThemesAll(); signals: void plasmaThemeSelected( const QString& id ); private: + /** @brief Intersect the list of enabled themes with the installed ones. */ + void winnowThemes(); + /** @brief Get the translated names for all enabled themes. */ + void updateThemeNames(); + /** @brief show enabled themes in the UI. */ + void fillUi(); + Ui::PlasmaLnfPage* ui; QString m_lnfPath; - PlasmaLnfList m_availableLnf; + ThemeInfoList m_enabledThemes; + + QButtonGroup *m_buttonGroup; + QList< ThemeWidget* > m_widgets; }; #endif //PLASMALNFPAGE_H diff --git a/src/modules/plasmalnf/PlasmaLnfViewStep.cpp b/src/modules/plasmalnf/PlasmaLnfViewStep.cpp index 40c8a5c4a..791ca0e6f 100644 --- a/src/modules/plasmalnf/PlasmaLnfViewStep.cpp +++ b/src/modules/plasmalnf/PlasmaLnfViewStep.cpp @@ -19,6 +19,7 @@ #include "PlasmaLnfJob.h" #include "PlasmaLnfPage.h" +#include "ThemeInfo.h" #include "utils/CalamaresUtils.h" #include "utils/Logger.h" @@ -109,8 +110,13 @@ PlasmaLnfViewStep::jobs() const Calamares::JobList l; cDebug() << "Creating Plasma LNF jobs .."; - if ( !m_themeId.isEmpty() && !m_lnfPath.isEmpty() ) - l.append( Calamares::job_ptr( new PlasmaLnfJob( m_lnfPath, m_themeId ) ) ); + if ( !m_themeId.isEmpty() ) + { + if ( !m_lnfPath.isEmpty() ) + l.append( Calamares::job_ptr( new PlasmaLnfJob( m_lnfPath, m_themeId ) ) ); + else + cDebug() << "WARNING: no lnftool given for plasmalnf module."; + } return l; } @@ -125,12 +131,41 @@ PlasmaLnfViewStep::setConfigurationMap( const QVariantMap& configurationMap ) cDebug() << "WARNING: no lnftool given for plasmalnf module."; m_liveUser = CalamaresUtils::getString( configurationMap, "liveuser" ); + + if ( configurationMap.contains( "themes" ) && + configurationMap.value( "themes" ).type() == QVariant::List ) + { + ThemeInfoList allThemes; + auto themeList = configurationMap.value( "themes" ).toList(); + // Create the ThemInfo objects for the listed themes; information + // about the themes from Plasma (e.g. human-readable name and description) + // are filled in by update_names() in PlasmaLnfPage. + for ( const auto& i : themeList ) + if ( i.type() == QVariant::Map ) + { + auto iv = i.toMap(); + allThemes.append( ThemeInfo( iv.value( "theme" ).toString(), iv.value( "image" ).toString() ) ); + } + else if ( i.type() == QVariant::String ) + allThemes.append( ThemeInfo( i.toString() ) ); + + if ( allThemes.length() == 1 ) + cDebug() << "WARNING: only one theme enabled in plasmalnf"; + m_widget->setEnabledThemes( allThemes ); + } + else + m_widget->setEnabledThemesAll(); // All of them } void PlasmaLnfViewStep::themeSelected( const QString& id ) { m_themeId = id; + if ( m_lnfPath.isEmpty() ) + { + cDebug() << "WARNING: no lnftool given for plasmalnf module."; + return; + } QProcess lnftool; if ( !m_liveUser.isEmpty() ) diff --git a/src/modules/plasmalnf/ThemeInfo.h b/src/modules/plasmalnf/ThemeInfo.h new file mode 100644 index 000000000..b186b9be1 --- /dev/null +++ b/src/modules/plasmalnf/ThemeInfo.h @@ -0,0 +1,97 @@ +/* === This file is part of Calamares - === + * + * Copyright 2017, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 . + */ + +#ifndef PLASMALNF_THEMEINFO_H +#define PLASMALNF_THEMEINFO_H + +#include +#include + +class KPluginMetaData; +class ThemeWidget; + +/** @brief describes a single plasma LnF theme. + * + * A theme description has an id, which is really the name of the desktop + * file (e.g. org.kde.breeze.desktop), a name which is human-readable and + * translated, and an optional image Page, which points to a local screenshot + * of that theme. + */ +struct ThemeInfo +{ + QString id; + QString name; + QString description; + QString imagePath; + ThemeWidget* widget; + + ThemeInfo() + : widget( nullptr ) + {} + + explicit ThemeInfo( const QString& _id ) + : id( _id ) + , widget( nullptr ) + { + } + + explicit ThemeInfo( const QString& _id, const QString& image ) + : id( _id ) + , imagePath( image ) + , widget( nullptr ) + {} + + // Defined in PlasmaLnfPage.cpp + explicit ThemeInfo( const KPluginMetaData& ); + + bool isValid() const { return !id.isEmpty(); } +} ; + +class ThemeInfoList : public QList< ThemeInfo > +{ +public: + /** @brief Looks for a given @p id in the list of themes, returns nullptr if not found. */ + ThemeInfo* findById( const QString& id ) + { + for ( ThemeInfo& i : *this ) + { + if ( i.id == id ) + return &i; + } + return nullptr; + } + + /** @brief Looks for a given @p id in the list of themes, returns nullptr if not found. */ + const ThemeInfo* findById( const QString& id ) const + { + for ( const ThemeInfo& i : *this ) + { + if ( i.id == id ) + return &i; + } + return nullptr; + } + + /** @brief Checks if a given @p id is in the list of themes. */ + bool contains( const QString& id ) const + { + return findById( id ) != nullptr; + } +} ; + +#endif diff --git a/src/modules/plasmalnf/ThemeWidget.cpp b/src/modules/plasmalnf/ThemeWidget.cpp new file mode 100644 index 000000000..28e01c2ff --- /dev/null +++ b/src/modules/plasmalnf/ThemeWidget.cpp @@ -0,0 +1,85 @@ +/* === This file is part of Calamares - === + * + * Copyright 2017, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 . + */ + +#include "ThemeWidget.h" + +#include "ThemeInfo.h" + +#include "utils/Logger.h" + +#include +#include +#include + +ThemeWidget::ThemeWidget(const ThemeInfo& info, QWidget* parent) + : QWidget( parent ) + , m_check( new QRadioButton( info.name.isEmpty() ? info.id : info.name, parent ) ) + , m_description( new QLabel( info.description, parent ) ) + , m_id( info.id ) +{ + QHBoxLayout* layout = new QHBoxLayout( this ); + this->setLayout( layout ); + + layout->addWidget( m_check, 1 ); + + constexpr QSize image_size{120, 80}; + + QPixmap image( info.imagePath ); + if ( info.imagePath.isEmpty() ) + { + // Image can't possibly be valid + image = QPixmap( ":/view-preview.png" ); + } + else if ( image.isNull() ) + { + // Not found or not specified, so convert the name into some (horrible, likely) + // color instead. + image = QPixmap( image_size ); + uint hash_color = qHash( info.imagePath.isEmpty() ? info.id : info.imagePath ); + cDebug() << "Theme image" << info.imagePath << "not found, hash" << hash_color; + image.fill( QColor( QRgb( hash_color ) ) ); + } + else + image.scaled( image_size ); + + QLabel* image_label = new QLabel( this ); + image_label->setPixmap( image ); + layout->addWidget( image_label, 1 ); + layout->addWidget( m_description, 3 ); + + connect( m_check, &QRadioButton::clicked, this, &ThemeWidget::clicked ); +} + +void +ThemeWidget::clicked( bool checked ) +{ + if ( checked ) + emit themeSelected( m_id ); +} + +QAbstractButton* +ThemeWidget::button() const +{ + return m_check; +} + +void ThemeWidget::updateThemeName(const ThemeInfo& info) +{ + m_check->setText( info.name ); + m_description->setText( info.description ); +} diff --git a/src/modules/plasmalnf/ThemeWidget.h b/src/modules/plasmalnf/ThemeWidget.h new file mode 100644 index 000000000..a6716ce72 --- /dev/null +++ b/src/modules/plasmalnf/ThemeWidget.h @@ -0,0 +1,53 @@ +/* === This file is part of Calamares - === + * + * Copyright 2017, Adriaan de Groot + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 . + */ + +#ifndef PLASMALNF_THEMEWIDGET_H +#define PLASMALNF_THEMEWIDGET_H + +#include + +class QAbstractButton; +class QLabel; +class QRadioButton; + +struct ThemeInfo; + +class ThemeWidget : public QWidget +{ + Q_OBJECT +public: + explicit ThemeWidget( const ThemeInfo& info, QWidget* parent = nullptr ); + + QAbstractButton* button() const; + + void updateThemeName( const ThemeInfo& info ); + +signals: + void themeSelected( const QString& id ); + +public slots: + void clicked( bool ); + +private: + QString m_id; + QRadioButton* m_check; + QLabel* m_description; +} ; + +#endif + diff --git a/src/modules/plasmalnf/page_plasmalnf.qrc b/src/modules/plasmalnf/page_plasmalnf.qrc index 7646d2b36..c63ecc03b 100644 --- a/src/modules/plasmalnf/page_plasmalnf.qrc +++ b/src/modules/plasmalnf/page_plasmalnf.qrc @@ -1 +1,5 @@ - + + + view-preview.png + + diff --git a/src/modules/plasmalnf/page_plasmalnf.ui b/src/modules/plasmalnf/page_plasmalnf.ui index 340527ad0..6da6647fd 100644 --- a/src/modules/plasmalnf/page_plasmalnf.ui +++ b/src/modules/plasmalnf/page_plasmalnf.ui @@ -13,13 +13,9 @@ Form - + - - margin-bottom: 1ex; -margin-left: 2em; - Placeholder @@ -28,9 +24,6 @@ margin-left: 2em; - - - diff --git a/src/modules/plasmalnf/plasmalnf.conf b/src/modules/plasmalnf/plasmalnf.conf index 1f0c72b25..8f395338c 100644 --- a/src/modules/plasmalnf/plasmalnf.conf +++ b/src/modules/plasmalnf/plasmalnf.conf @@ -10,11 +10,30 @@ # in exec, so that the target user has been created alrady. --- # Full path to the Plasma look-and-feel tool (CLI program -# for querying and applying Plasma themes). +# for querying and applying Plasma themes). If this is not +# set, no LNF setting will happen. lnftool: "/usr/bin/lookandfeeltool" # For systems where the user Calamares runs as (usually root, # via either sudo or pkexec) has a clean environment, set this # to the originating username; the lnftool will be run through # "sudo -H -u " instead of directly. -liveuser: "live" +# +# liveuser: "live" + +# You can limit the list of Plasma look-and-feel themes by listing ids +# here. If this key is not present, all of the installed themes are listed. +# If the key is present, only installed themes that are *also* included +# in the list are shown (could be none!). +# +# Themes may be listed by id, (e.g. fluffy-bunny, below) or as a theme +# and an image (e.g. breeze) which will be used to show a screenshot. +# Themes with no image get a "missing screenshot" image; if the +# image file is not found, they get a color swatch based on the image name. +themes: + - org.kde.fuzzy-pig.desktop + - theme: org.kde.breeze.desktop + image: "breeze.png" + - theme: org.kde.breezedark.desktop + image: "breeze-dark.png" + - org.kde.fluffy-bunny.desktop diff --git a/src/modules/plasmalnf/view-preview.png b/src/modules/plasmalnf/view-preview.png new file mode 100644 index 000000000..8e5f07ba9 Binary files /dev/null and b/src/modules/plasmalnf/view-preview.png differ diff --git a/src/modules/plasmalnf/view-preview.svg b/src/modules/plasmalnf/view-preview.svg new file mode 100644 index 000000000..90e5beec5 --- /dev/null +++ b/src/modules/plasmalnf/view-preview.svg @@ -0,0 +1,13 @@ + + + + + +