[packagechooser] add Office example settings

This commit is contained in:
Philip Müller 2019-09-02 11:36:57 -04:00
parent 42d6322ce3
commit a927a9f6a2
18 changed files with 21 additions and 1300 deletions

View File

@ -1,45 +0,0 @@
find_package( Qt5 COMPONENTS Core Gui Widgets REQUIRED )
set( _extra_libraries "" )
### OPTIONAL AppData XML support in PackageModel
#
#
find_package(Qt5 COMPONENTS Xml)
if ( Qt5Xml_FOUND )
add_definitions( -DHAVE_XML )
list( APPEND _extra_libraries Qt5::Xml )
endif()
calamares_add_plugin( officechooser
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
PackageChooserPage.cpp
PackageChooserViewStep.cpp
PackageModel.cpp
RESOURCES
packagechooser.qrc
UI
page_package.ui
LINK_PRIVATE_LIBRARIES
calamaresui
${_extra_libraries}
SHARED_LIB
)
if( ECM_FOUND AND BUILD_TESTING )
ecm_add_test(
Tests.cpp
TEST_NAME
officechoosertest
LINK_LIBRARIES
${CALAMARES_LIBRARIES}
calamares_viewmodule_officechooser
Qt5::Core
Qt5::Test
Qt5::Gui
${_extra_libraries}
)
calamares_automoc( officechoosertest)
endif()

View File

@ -1,144 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Philip MÜller <philm@manjaro.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 "PackageChooserPage.h"
#include "ui_page_package.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include <QLabel>
PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent )
: QWidget( parent )
, ui( new Ui::PackageChooserPage )
, m_introduction( QString(),
QString(),
tr( "Office Suite" ),
tr( "Please pick an office suite from the list. The selected product will be installed." ) )
{
m_introduction.screenshot = QPixmap( QStringLiteral( ":/images/choose-office.jpg" ) );
ui->setupUi( this );
CALAMARES_RETRANSLATE( updateLabels(); )
switch ( mode )
{
case PackageChooserMode::Optional:
FALLTHRU;
case PackageChooserMode::Required:
ui->products->setSelectionMode( QAbstractItemView::SingleSelection );
break;
case PackageChooserMode::OptionalMultiple:
FALLTHRU;
case PackageChooserMode::RequiredMultiple:
ui->products->setSelectionMode( QAbstractItemView::ExtendedSelection );
}
}
void
PackageChooserPage::currentChanged( const QModelIndex& index )
{
if ( !index.isValid() || !ui->products->selectionModel()->hasSelection() )
{
ui->productName->setText( m_introduction.name.get() );
ui->productScreenshot->setPixmap( m_introduction.screenshot );
ui->productDescription->setText( m_introduction.description.get() );
}
else
{
const auto* model = ui->products->model();
ui->productName->setText( model->data( index, PackageListModel::NameRole ).toString() );
ui->productScreenshot->setPixmap( model->data( index, PackageListModel::ScreenshotRole ).value< QPixmap >() );
ui->productDescription->setText( model->data( index, PackageListModel::DescriptionRole ).toString() );
}
}
void
PackageChooserPage::updateLabels()
{
if ( ui && ui->products && ui->products->selectionModel() )
{
currentChanged( ui->products->selectionModel()->currentIndex() );
}
else
{
currentChanged( QModelIndex() );
}
emit selectionChanged();
}
void
PackageChooserPage::setModel( QAbstractItemModel* model )
{
ui->products->setModel( model );
// Check if any of the items in the model is the "none" option.
// If so, copy its values into the introduction / none item.
for ( int r = 0; r < model->rowCount(); ++r )
{
auto index = model->index( r, 0 );
if ( index.isValid() )
{
QVariant v = model->data( index, PackageListModel::IdRole );
if ( v.isValid() && v.toString().isEmpty() )
{
m_introduction.name = model->data( index, PackageListModel::NameRole ).toString();
m_introduction.description = model->data( index, PackageListModel::DescriptionRole ).toString();
m_introduction.screenshot = model->data( index, PackageListModel::ScreenshotRole ).value< QPixmap >();
currentChanged( QModelIndex() );
break;
}
}
}
connect( ui->products->selectionModel(),
&QItemSelectionModel::selectionChanged,
this,
&PackageChooserPage::updateLabels );
}
bool
PackageChooserPage::hasSelection() const
{
return ui && ui->products && ui->products->selectionModel() && ui->products->selectionModel()->hasSelection();
}
QStringList
PackageChooserPage::selectedPackageIds() const
{
if ( !( ui && ui->products && ui->products->selectionModel() ) )
{
return QStringList();
}
const auto* model = ui->products->model();
QStringList ids;
for ( const auto& index : ui->products->selectionModel()->selectedIndexes() )
{
QString pid = model->data( index, PackageListModel::IdRole ).toString();
if ( !pid.isEmpty() )
{
ids.append( pid );
}
}
return ids;
}

View File

@ -1,60 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PACKAGECHOOSERPAGE_H
#define PACKAGECHOOSERPAGE_H
#include "PackageModel.h"
#include <QAbstractItemModel>
#include <QWidget>
namespace Ui
{
class PackageChooserPage;
}
class PackageChooserPage : public QWidget
{
Q_OBJECT
public:
explicit PackageChooserPage( PackageChooserMode mode, QWidget* parent = nullptr );
void setModel( QAbstractItemModel* model );
/// @brief Is anything selected?
bool hasSelection() const;
/** @brief Get the list of selected ids
*
* This list may be empty (if none is selected).
*/
QStringList selectedPackageIds() const;
public slots:
void currentChanged( const QModelIndex& index );
void updateLabels();
signals:
void selectionChanged();
private:
Ui::PackageChooserPage* ui;
PackageItem m_introduction;
};
#endif // PACKAGECHOOSERPAGE_H

View File

@ -1,240 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Philip MÜller <philm@manjaro.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 "PackageChooserViewStep.h"
#include "PackageChooserPage.h"
#include "PackageModel.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#include <QDesktopServices>
#include <QVariantMap>
CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserViewStepFactory, registerPlugin< PackageChooserViewStep >(); )
PackageChooserViewStep::PackageChooserViewStep( QObject* parent )
: Calamares::ViewStep( parent )
, m_widget( nullptr )
, m_model( nullptr )
, m_mode( PackageChooserMode::Required )
{
emit nextStatusChanged( false );
}
PackageChooserViewStep::~PackageChooserViewStep()
{
if ( m_widget && m_widget->parent() == nullptr )
{
m_widget->deleteLater();
}
delete m_model;
}
QString
PackageChooserViewStep::prettyName() const
{
return tr( "Office Suite" );
}
QWidget*
PackageChooserViewStep::widget()
{
if ( !m_widget )
{
m_widget = new PackageChooserPage( m_mode, nullptr );
connect( m_widget, &PackageChooserPage::selectionChanged, [=]() {
emit nextStatusChanged( this->isNextEnabled() );
} );
if ( m_model )
{
hookupModel();
}
else
{
cWarning() << "OfficeChooser Widget created before model.";
}
}
return m_widget;
}
bool
PackageChooserViewStep::isNextEnabled() const
{
if ( !m_model )
{
return false;
}
if ( !m_widget )
{
// No way to have changed anything
return true;
}
switch ( m_mode )
{
case PackageChooserMode::Optional:
case PackageChooserMode::OptionalMultiple:
// zero or one OR zero or more
return true;
case PackageChooserMode::Required:
case PackageChooserMode::RequiredMultiple:
// exactly one OR one or more
return m_widget->hasSelection();
}
NOTREACHED return true;
}
bool
PackageChooserViewStep::isBackEnabled() const
{
return true;
}
bool
PackageChooserViewStep::isAtBeginning() const
{
return true;
}
bool
PackageChooserViewStep::isAtEnd() const
{
return true;
}
void
PackageChooserViewStep::onLeave()
{
QString key = QStringLiteral( "packagechooser_%1" ).arg( m_id );
QString value;
if ( m_widget->hasSelection() )
{
value = m_widget->selectedPackageIds().join( ',' );
}
Calamares::JobQueue::instance()->globalStorage()->insert( key, value );
cDebug() << "OfficeChooser" << key << "selected" << value;
}
Calamares::JobList
PackageChooserViewStep::jobs() const
{
Calamares::JobList l;
return l;
}
void
PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
QString mode = CalamaresUtils::getString( configurationMap, "mode" );
bool ok = false;
if ( !mode.isEmpty() )
{
m_mode = roleNames().find( mode, ok );
}
if ( !ok )
{
m_mode = PackageChooserMode::Required;
}
m_id = CalamaresUtils::getString( configurationMap, "id" );
if ( m_id.isEmpty() )
{
// Not set, so use the instance id
// TODO: use a stronger type than QString for structured IDs
m_id = moduleInstanceKey().split( '@' ).last();
}
bool first_time = !m_model;
if ( configurationMap.contains( "items" ) )
{
fillModel( configurationMap.value( "items" ).toList() );
}
if ( first_time && m_widget && m_model )
{
hookupModel();
}
}
void
PackageChooserViewStep::fillModel( const QVariantList& items )
{
if ( !m_model )
{
m_model = new PackageListModel( nullptr );
}
if ( items.isEmpty() )
{
cWarning() << "No *items* for OfficeChooser module.";
return;
}
cDebug() << "Loading OfficeChooser model items from config";
int item_index = 0;
for ( const auto& item_it : items )
{
++item_index;
QVariantMap item_map = item_it.toMap();
if ( item_map.isEmpty() )
{
cWarning() << "OfficeChooser entry" << item_index << "is not valid.";
continue;
}
if ( item_map.contains( "appdata" ) )
{
m_model->addPackage( PackageItem::fromAppData( item_map ) );
}
else
{
m_model->addPackage( PackageItem( item_map ) );
}
}
}
void
PackageChooserViewStep::hookupModel()
{
if ( !m_model || !m_widget )
{
cError() << "Can't hook up model until widget and model both exist.";
return;
}
m_widget->setModel( m_model );
}

View File

@ -1,72 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PACKAGECHOOSERVIEWSTEP_H
#define PACKAGECHOOSERVIEWSTEP_H
#include "PluginDllMacro.h"
#include "utils/PluginFactory.h"
#include "viewpages/ViewStep.h"
#include "PackageModel.h"
#include <QObject>
#include <QUrl>
#include <QVariantMap>
class PackageChooserPage;
class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep
{
Q_OBJECT
public:
explicit PackageChooserViewStep( QObject* parent = nullptr );
virtual ~PackageChooserViewStep() override;
QString prettyName() const override;
QWidget* widget() override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
void onLeave() override;
Calamares::JobList jobs() const override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
private:
void fillModel( const QVariantList& items );
void hookupModel();
PackageChooserPage* m_widget;
PackageListModel* m_model;
// Configuration
PackageChooserMode m_mode;
QString m_id;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory )
#endif // PACKAGECHOOSERVIEWSTEP_H

View File

@ -1,374 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PackageModel.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
#ifdef HAVE_XML
#include <QDomDocument>
#include <QDomNodeList>
#include <QFile>
#endif
const NamedEnumTable< PackageChooserMode >&
roleNames()
{
static const NamedEnumTable< PackageChooserMode > names {
{ "optional", PackageChooserMode::Optional },
{ "required", PackageChooserMode::Required },
{ "optionalmultiple", PackageChooserMode::OptionalMultiple },
{ "requiredmultiple", PackageChooserMode::RequiredMultiple },
// and a bunch of aliases
{ "zero-or-one", PackageChooserMode::Optional },
{ "radio", PackageChooserMode::Required },
{ "one", PackageChooserMode::Required },
{ "set", PackageChooserMode::OptionalMultiple },
{ "zero-or-more", PackageChooserMode::OptionalMultiple },
{ "multiple", PackageChooserMode::RequiredMultiple },
{ "one-or-more", PackageChooserMode::RequiredMultiple }
};
return names;
}
PackageItem::PackageItem() {}
PackageItem::PackageItem( const QString& a_id,
const QString& a_package,
const QString& a_name,
const QString& a_description )
: id( a_id )
, package( a_package )
, name( a_name )
, description( a_description )
{
}
PackageItem::PackageItem( const QString& a_id,
const QString& a_package,
const QString& a_name,
const QString& a_description,
const QString& screenshotPath )
: id( a_id )
, package( a_package )
, name( a_name )
, description( a_description )
, screenshot( screenshotPath )
{
}
PackageItem::PackageItem::PackageItem( const QVariantMap& item_map )
: id( CalamaresUtils::getString( item_map, "id" ) )
, package( CalamaresUtils::getString( item_map, "package" ) )
, name( CalamaresUtils::Locale::TranslatedString( item_map, "name" ) )
, description( CalamaresUtils::Locale::TranslatedString( item_map, "description" ) )
, screenshot( CalamaresUtils::getString( item_map, "screenshot" ) )
{
if ( name.isEmpty() && id.isEmpty() )
{
name = QObject::tr( "No product" );
}
else if ( name.isEmpty() )
{
cWarning() << "PackageChooser item" << id << "has an empty name.";
}
if ( description.isEmpty() )
{
description = QObject::tr( "No description provided." );
}
}
#ifdef HAVE_XML
/** @brief try to load the given @p fileName XML document
*
* Returns a QDomDocument, which will be valid iff the file can
* be read and contains valid XML data.
*/
static inline QDomDocument
loadAppData( const QString& fileName )
{
QFile file( fileName );
if ( !file.open( QIODevice::ReadOnly ) )
{
return QDomDocument();
}
QDomDocument doc( "AppData" );
if ( !doc.setContent( &file ) )
{
file.close();
return QDomDocument();
}
file.close();
return doc;
}
/** @brief gets the text of child element @p tagName
*/
static inline QString
getChildText( const QDomNode& n, const QString& tagName )
{
QDomElement e = n.firstChildElement( tagName );
return e.isNull() ? QString() : e.text();
}
/** @brief Gets a suitable screenshot path
*
* The <screenshots> element contains zero or more <screenshot>
* elements, which can have a *type* associated with them.
* Scan the screenshot elements, return the <image> path
* for the one labeled with type=default or, if there is no
* default, the first element.
*/
static inline QString
getScreenshotPath( const QDomNode& n )
{
QDomElement shotsNode = n.firstChildElement( "screenshots" );
if ( shotsNode.isNull() )
{
return QString();
}
const QDomNodeList shotList = shotsNode.childNodes();
int firstScreenshot = -1; // Use which screenshot node?
for ( int i = 0; i < shotList.count(); ++i )
{
if ( !shotList.at( i ).isElement() )
{
continue;
}
QDomElement e = shotList.at( i ).toElement();
if ( e.tagName() != "screenshot" )
{
continue;
}
// If none has the "type=default" attribute, use the first one
if ( firstScreenshot < 0 )
{
firstScreenshot = i;
}
// But type=default takes precedence.
if ( e.hasAttribute( "type" ) && e.attribute( "type" ) == "default" )
{
firstScreenshot = i;
break;
}
}
if ( firstScreenshot >= 0 )
{
return shotList.at( firstScreenshot ).firstChildElement( "image" ).text();
}
return QString();
}
/** @brief Returns language of the given element @p e
*
* Transforms the attribute value for xml:lang to something
* suitable for TranslatedString (e.g. [lang]).
*/
static inline QString
getLanguage( const QDomElement& e )
{
QString language = e.attribute( "xml:lang" );
if ( !language.isEmpty() )
{
language.replace( '-', '_' );
language.prepend( '[' );
language.append( ']' );
}
return language;
}
/** @brief Scan the list of @p children for @p tagname elements and add them to the map
*
* Uses @p mapname instead of @p tagname for the entries in map @p m
* to allow renaming from XML to map keys (in particular for
* TranslatedString). Also transforms xml:lang attributes to suitable
* key-decorations on @p mapname.
*/
static inline void
fillMap( QVariantMap& m, const QDomNodeList& children, const QString& tagname, const QString& mapname )
{
for ( int i = 0; i < children.count(); ++i )
{
if ( !children.at( i ).isElement() )
{
continue;
}
QDomElement e = children.at( i ).toElement();
if ( e.tagName() != tagname )
{
continue;
}
m[ mapname + getLanguage( e ) ] = e.text();
}
}
/** @brief gets the <name> and <description> elements
*
* Builds up a map of the <name> elements (which may have a *lang*
* attribute to indicate translations and paragraphs of the
* <description> element (also with lang). Uses the <summary>
* elements to supplement the description if no description
* is available for a given language.
*
* Returns a map with keys suitable for use by TranslatedString.
*/
static inline QVariantMap
getNameAndSummary( const QDomNode& n )
{
QVariantMap m;
const QDomNodeList children = n.childNodes();
fillMap( m, children, "name", "name" );
fillMap( m, children, "summary", "description" );
const QDomElement description = n.firstChildElement( "description" );
if ( !description.isNull() )
{
fillMap( m, description.childNodes(), "p", "description" );
}
return m;
}
#endif
PackageItem
PackageItem::fromAppData( const QVariantMap& item_map )
{
#ifdef HAVE_XML
QString fileName = CalamaresUtils::getString( item_map, "appdata" );
if ( fileName.isEmpty() )
{
cWarning() << "Can't load AppData without a suitable key.";
return PackageItem();
}
cDebug() << "Loading AppData XML from" << fileName;
QDomDocument doc = loadAppData( fileName );
if ( doc.isNull() )
{
return PackageItem();
}
QDomElement componentNode = doc.documentElement();
if ( !componentNode.isNull() && componentNode.tagName() == "component" )
{
// An "id" entry in the Calamares config overrides ID in the AppData
QString id = CalamaresUtils::getString( item_map, "id" );
if ( id.isEmpty() )
{
id = getChildText( componentNode, "id" );
}
if ( id.isEmpty() )
{
return PackageItem();
}
// A "screenshot" entry in the Calamares config overrides AppData
QString screenshotPath = CalamaresUtils::getString( item_map, "screenshot" );
if ( screenshotPath.isEmpty() )
{
screenshotPath = getScreenshotPath( componentNode );
}
QVariantMap map = getNameAndSummary( componentNode );
map.insert( "id", id );
map.insert( "screenshot", screenshotPath );
return PackageItem( map );
}
return PackageItem();
#else
cWarning() << "Loading AppData XML is not supported.";
return PackageItem();
#endif
}
PackageListModel::PackageListModel( QObject* parent )
: QAbstractListModel( parent )
{
}
PackageListModel::PackageListModel( PackageList&& items, QObject* parent )
: QAbstractListModel( parent )
, m_packages( std::move( items ) )
{
}
PackageListModel::~PackageListModel() {}
void
PackageListModel::addPackage( PackageItem&& p )
{
// Only add valid packages
if ( p.isValid() )
{
int c = m_packages.count();
beginInsertRows( QModelIndex(), c, c );
m_packages.append( p );
endInsertRows();
}
}
int
PackageListModel::rowCount( const QModelIndex& index ) const
{
// For lists, valid indexes have zero children; only the root index has them
return index.isValid() ? 0 : m_packages.count();
}
QVariant
PackageListModel::data( const QModelIndex& index, int role ) const
{
if ( !index.isValid() )
{
return QVariant();
}
int row = index.row();
if ( row >= m_packages.count() || row < 0 )
{
return QVariant();
}
if ( role == Qt::DisplayRole /* Also PackageNameRole */ )
{
return m_packages[ row ].name.get();
}
else if ( role == DescriptionRole )
{
return m_packages[ row ].description.get();
}
else if ( role == ScreenshotRole )
{
return m_packages[ row ].screenshot;
}
else if ( role == IdRole )
{
return m_packages[ row ].id;
}
return QVariant();
}

View File

@ -1,128 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PACKAGEMODEL_H
#define PACKAGEMODEL_H
#include "locale/TranslatableConfiguration.h"
#include "utils/NamedEnum.h"
#include <QAbstractListModel>
#include <QObject>
#include <QPixmap>
#include <QVector>
enum class PackageChooserMode
{
Optional, // zero or one
Required, // exactly one
OptionalMultiple, // zero or more
RequiredMultiple // one or more
};
const NamedEnumTable< PackageChooserMode >& roleNames();
struct PackageItem
{
QString id;
// FIXME: unused
QString package;
CalamaresUtils::Locale::TranslatedString name;
CalamaresUtils::Locale::TranslatedString description;
QPixmap screenshot;
/// @brief Create blank PackageItem
PackageItem();
/** @brief Creates a PackageItem from given strings
*
* This constructor sets all the text members,
* but leaves the screenshot blank. Set that separately.
*/
PackageItem( const QString& id, const QString& package, const QString& name, const QString& description );
/** @brief Creates a PackageItem from given strings.
*
* Set all the text members and load the screenshot from the given
* @p screenshotPath, which may be a QRC path (:/path/in/qrc) or
* a filesystem path, whatever QPixmap understands.
*/
PackageItem( const QString& id,
const QString& package,
const QString& name,
const QString& description,
const QString& screenshotPath );
/** @brief Creates a PackageItem from a QVariantMap
*
* This is intended for use when loading PackageItems from a
* configuration map. It will look up the various keys in the map
* and handle translation strings as well.
*/
PackageItem( const QVariantMap& map );
/** @brief Is this item valid?
*
* A valid item has an untranslated name available.
*/
bool isValid() const { return !name.isEmpty(); }
/** @brief Loads an AppData XML file and returns a PackageItem
*
* The @p map must have a key *appdata*. That is used as the
* primary source of information, but keys *id* and *screenshotPath*
* may be used to override parts of the AppData -- so that the
* ID is under the control of Calamares, and the screenshot can be
* forced to a local path available on the installation medium.
*
* Requires XML support in libcalamares, if not present will
* return invalid PackageItems.
*/
static PackageItem fromAppData( const QVariantMap& map );
};
using PackageList = QVector< PackageItem >;
class PackageListModel : public QAbstractListModel
{
public:
PackageListModel( PackageList&& items, QObject* parent );
PackageListModel( QObject* parent );
virtual ~PackageListModel() override;
/** @brief Add a package @p to the model
*
* Only valid packages are added -- that is, they must have a name.
*/
void addPackage( PackageItem&& p );
int rowCount( const QModelIndex& index ) const override;
QVariant data( const QModelIndex& index, int role ) const override;
enum Roles : int
{
NameRole = Qt::DisplayRole,
DescriptionRole = Qt::UserRole,
ScreenshotRole,
IdRole
};
private:
PackageList m_packages;
};
#endif

View File

@ -1,87 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Tests.h"
#include "PackageModel.h"
#include "utils/Logger.h"
#include <QtTest/QtTest>
QTEST_MAIN( PackageChooserTests )
PackageChooserTests::PackageChooserTests() {}
PackageChooserTests::~PackageChooserTests() {}
void
PackageChooserTests::initTestCase()
{
Logger::setupLogLevel( Logger::LOGDEBUG );
}
void
PackageChooserTests::testBogus()
{
QVERIFY( true );
}
void
PackageChooserTests::testAppData()
{
// Path from the build-dir and from the running-the-test varies,
// for in-source build, for build/, and for tests-in-build/,
// so look in multiple places.
QString appdataName( "io.calamares.calamares.appdata.xml" );
for ( const auto& prefix : QStringList { "", "../", "../../../", "../../../../" } )
{
if ( QFile::exists( prefix + appdataName ) )
{
appdataName = prefix + appdataName;
break;
}
}
QVERIFY( QFile::exists( appdataName ) );
QVariantMap m;
m.insert( "appdata", appdataName );
PackageItem p1 = PackageItem::fromAppData( m );
#ifdef HAVE_XML
QVERIFY( p1.isValid() );
QCOMPARE( p1.id, "io.calamares.calamares.desktop" );
QCOMPARE( p1.name.get(), "Calamares" );
// The <description> entry has precedence
QCOMPARE( p1.description.get(), "Calamares is an installer program for Linux distributions." );
// .. but en_GB doesn't have an entry in description, so uses <summary>
QCOMPARE( p1.description.get( QLocale( "en_GB" ) ), "Calamares Linux Installer" );
QCOMPARE( p1.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." );
QVERIFY( p1.screenshot.isNull() );
m.insert( "id", "calamares" );
m.insert( "screenshot", ":/images/calamares.png" );
PackageItem p2 = PackageItem::fromAppData( m );
QVERIFY( p2.isValid() );
QCOMPARE( p2.id, "calamares" );
QCOMPARE( p2.description.get( QLocale( "nl" ) ), "Calamares is een installatieprogramma voor Linux distributies." );
QVERIFY( !p2.screenshot.isNull() );
#else
QVERIFY( !p1.isValid() );
#endif
}

View File

@ -1,37 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PACKAGECHOOSERTESTS_H
#define PACKAGECHOOSERTESTS_H
#include <QObject>
class PackageChooserTests : public QObject
{
Q_OBJECT
public:
PackageChooserTests();
~PackageChooserTests() override;
private Q_SLOTS:
void initTestCase();
void testBogus();
void testAppData();
};
#endif

View File

@ -1,7 +0,0 @@
<RCC>
<qresource prefix="/">
<file>images/choose-office.jpg</file>
<file>images/LibreOffice.jpg</file>
<file>images/FreeOffice.jpg</file>
</qresource>
</RCC>

View File

@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PackageChooserPage</class>
<widget class="QWidget" name="PackageChooserPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>500</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">
<item>
<widget class="QListView" name="products">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,3,1">
<item>
<widget class="QLabel" name="productName">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="productScreenshot">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="productDescription">
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -30,8 +30,8 @@ mode: required
# with the explicit id "". # with the explicit id "".
# #
labels: labels:
step: "Packages" step: "Office Suite"
step[nl]: "Pakketten" step[de]: "Office-Paket"
# Items to display in the chooser. In general, this should be a # Items to display in the chooser. In general, this should be a
# pretty short list to avoid overwhelming the UI. This is a list # pretty short list to avoid overwhelming the UI. This is a list
@ -85,23 +85,22 @@ labels:
items: items:
- id: "" - id: ""
package: "" package: ""
name: "No Desktop" name: "No Office Suite"
name[nl]: "Geen desktop" name[de]: "Kein Office Paket"
description: "Please pick a desktop environment from the list. If you don't want to install a desktop, that's fine, your system will start up in text-only mode and you can install a desktop environment later." description: "Please pick an office suite from the list. If you don't want to install an office suite, that's fine, you can install one later as needed."
description[nl]: "Kies eventueel een desktop-omgeving uit deze lijst. Als u geen desktop-omgeving wenst te gebruiken, kies er dan geen. In dat geval start het systeem straks op in tekst-modus en kunt u later alsnog een desktop-omgeving installeren." description[de]: "Bitte wählen Sie ein Office-Paket von der Liste. Wenn Sie kein Office installieren wollen, kann dies auch zu einer späteren Zeitpunkt erfolgen."
screenshot: ":/images/no-selection.png" screenshot: ":/images/choose-office.jpg"
- id: kde - id: "libreoffice-still"
package: kde package: "libreoffice-still"
name: Plasma Desktop name: "LibreOffice"
description: "KDE Plasma Desktop, simple by default, a clean work area for real-world usage which intends to stay out of your way. Plasma is powerful when needed, enabling the user to create the workflow that makes them more effective to complete their tasks." name[de]: "LibreOffice"
screenshot: ":/images/kde.png" description: "LibreOffice is a powerful and free office suite, used by millions of people around the world. Its clean interface and feature-rich tools help you unleash your creativity and enhance your productivity."
- id: gnome description[de]: "LibreOffice ist ein leistungsstarkes Office-Paket. Die klare Oberfläche und mächtigen Werkzeuge lassen Sie Ihre Kreativität entfalten und Ihre Produktivität steigern.
package: gnome screenshot: ":/images/LibreOffice.jpg"
name: GNOME - id: "freeoffice"
description: GNU Networked Object Modeling Environment Desktop package: "freeoffice"
screenshot: ":/images/gnome.png" name: "FreeOffice"
- id: calamares name[de]: "FreeOffice"
appdata: ../io.calamares.calamares.appdata.xml description: "FreeOffice is a powerful Office suite with word processing, spreadsheet and presentation software. It is seamlessly compatible with Microsoft Office and can be used free of charge. For long-term use, request a free product key directly in the application."
screenshot: ":/images/calamares.png" description[de]: "FreeOffice ist ein leistungsstarkes Office-Paket mit Textverarbeitung, Tabellenkalkulation und Präsentationssoftware. Es ist nahtlos kompatible zu Microsoft Office und kann frei genutzt werden. Für eine Langzeitnutzung kann ein Produktkey im Programm angefordert werden."
- id: kate screenshot: ":/images/FreeOffice.jpg"
appstream: org.kde.kwrite.desktop