Enable usage of categorized repositories

This change allows to categorize repositories in component selection
page. Using categorized repositories will by default show only
uncagetorized repository items in tree. Selecting one or several categories
using checkbox will update the treeview to show all selected categorized repository
content. Repository's metadata is fetched only after the category is selected.
Categorized repositories can be defined in config.xml:
<RepositoryCategories>
    <RemoteRepositories>
    <Displayname>category 1</Displayname>
    <Repository> <Url>(url)</Url></Repository>
    </RemoteReposiories>
    ...
<RepositoryCategories

Change-Id: I6eae9daee70b1afa322144d52c11f25d0b655ebf
Reviewed-by: Jani Heikkinen <jani.heikkinen@qt.io>
Reviewed-by: Iikka Eklund <iikka.eklund@qt.io>
This commit is contained in:
Katja Marttila 2018-03-09 10:21:54 +02:00
parent b3d140a2c8
commit 0f8d11ca8e
39 changed files with 933 additions and 76 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,18 @@
\section1 Generating the Example Installer
To create the example installer, switch to the example source directory on
the command line and enter the following command:
\list
\li On Windows:
\code
..\..\bin\binarycreator.exe --online-only -c config\config.xml -p packages installer.exe
\endcode
\li On Linux or macOS:
\code
../../bin/binarycreator --online-only -c config/config.xml -p packages installer
\endcode
\endlist
You should now be able to run the installer and install from the repository.

View File

@ -265,6 +265,10 @@
\li List of remote repositories. This element can contain several \c <Repository> child
elements that each contain the \c <Url> child element that specifies the URL to
access the repository. For more information, see \l{Configuring Repositories}.
\row
\li RepositoryCategories
\li Name of a category that can contain a list of \c <RemoteRepositories> child elements.
For more information, see \l{Configuring Repository Categories}.
\row
\li MaintenanceToolName
\li Filename of the generated maintenance tool. Defaults to
@ -1114,6 +1118,36 @@
text. Authentication details not set here will be gotten at runtime using a dialog.
The user can work around these settings at runtime.
\section1 Configuring Repository Categories
The \c <RepositoryCategory> element in the installer configuration file
(config.xml) can contain a list of several \c <RemoteRepositories> elements. Each \c <RemoteRepositories>
element within the \c <RepositoryCagetory> element is considered a category, which has a \c <DisplayName> and can
contain several \c <Repository> elements. Repository categories are shown in the component selection page,
on the left side of the component selection widget:
\image ifw-repository-categories.png "Component selection Page"
By default, only repositories with no category are shown in the component selection widget. Checking one or
several repositories and pressing \uicontrol Fetch will update the widget to show content also
from the selected categorized repositories.
Example of creating a repository category:
\code
<RepositoryCategories>
<RemoteRepositories>
<Displayname>Category 1</Displayname>
<Repository>
<Url>http://www.example.com/packages</Url>
<Enabled>1</Enabled>
<Username>user</Username>
<Password>password</Password>
<DisplayName>Example repository</DisplayName>
</Repository>
</RemoteRepositories>
</RepositoryCategories>
\endcode
\section1 Creating Installer Binaries

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,117 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: http://www.gnu.org/copyleft/fdl.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example repositorycategories
\ingroup qtifwexamples
\title Repository Categories Example
\brief Using the RepositoryCategories element to set up an
online installer where repositories are grouped.
\image qtifw-examples-repository-categories.png
\e{Repository Categories} illustrates how to set up an installer
where repositories are grouped into categories.
\include installerfw-examples-configuring.qdocinc
\list
\li The \c <RepositoryCategories> element shows how to group repositories into categories.
\c <RepositoryCategories> can contain one or several \c <RemoteRepositories>
child elements that specify a connection to repositories. For more
information about \c <RemoteRepositories> see
\l{Configuring Repositories}.
\endlist
\quotefile repositorycategories/config/config.xml
\include installerfw-examples-packaging.qdocinc
\list
\li The \c <Default> element is set to \c true to preselect the
component in the installer.
\endlist
\quotefile online/packages/A/meta/package.xml
\section1 Generating the Online Repository
This installer contains four packages that each have two components. The \c Packages directory contains two components
that are not grouped categories. They are always visible in tree view in the component selection page. \c Packages_forcategory1
and \c packages2_forcategory1 both contain two components, which are visible when \c Category 1 is fetched. \c Packages_forcategory2
contains two components that are visible only when \c Category 2 is fetched.
The packages need to be converted to a file structure that the installer can
fetch at runtime. To use the \c repogen tool to convert the packages, switch
to the example source directory on the command line and enter the following
command:
\list
\li On Windows:
\code
..\..\bin\repogen.exe -p packages repository
..\..\bin\repogen.exe -p packages_forcategory1 repository1
..\..\bin\repogen.exe -p packages2_forcategory1 repository2
..\..\bin\repogen.exe -p packages_forcategory2 repository3
\endcode
\li On Linux or macOS:
\code
../../bin/repogen -p packages repository
../../bin/repogen -p packages_forcategory1 repository1
../../bin/repogen -p packages2_forcategory1 repository2
../../bin/repogen -p packages_forcategory2 repository3
\endcode
\endlist
The generated \c repository, \c repository1, \c repository2 and \c repository3 directories will now
contain a full copy of the package data and some additionally generated metadata, such as SHA
checksums.
The directories now need to be made available at the URL set in
\c config.xml: \c{http://localhost/repository}, \c{http://localhost/repository1}, \c{http://localhost/repository2} and
\c{http://localhost/repository3}. How this is done depends on
the platform and web server used. If you do not have a running web server
yet, but have Python available, you should be able to start a minimal web
server from the command line. Make sure you are in the example directory,
and then enter:
\code
python -m SimpleHTTPServer 80
\endcode
You should now be able to open and explore \l{http://localhost/repository}
in your web browser.
\note If you do not have enough permissions to set up a web server locally,
you can also specify an absolute \c{file:///} URL as the value of the \c URL
element in \c config.xml. For example,
\c file:///C:/Qt/QtIFW/examples/repositorycategories/repository would be a valid URL on
Windows if \c repository is located in \c C:\Qt\QtIFW\examples\repositorycategories.
\include installerfw-examples-generating-online.qdocinc
*/

View File

@ -11,6 +11,7 @@ SUBDIRS += \
openreadme \
quitinstaller \
registerfileextension \
repositorycategories \
startmenu \
systeminfo \
stylesheet

View File

@ -0,0 +1,30 @@
Shows how to set up an online installer and how to use categorized repositories. Categorized repositories are not loaded to the tree view by default, instead you can select
to show categorized repositories in a tree view combobox. By default, repositories without categories are always shown in the tree view.
The example uses a very simple web server shipped with Python.
Generate the online repositories with
repogen -p packages repository
repogen -p packages_forcategory1 repository1
repogen -p packages2_forcategory1 repository2
repogen -p packages_forcategory2 repository3
Generate the installer with
binarycreator --online-only -c config/config.xml -p packages installer
Now launch a minimal web server in the example's directory (admin rights may be needed)
python -m SimpleHTTPServer 80
This should make the content of the local directory available under
http://localhost
You should be able to now launch the installer.
To deploy an update, run
repogen --update-new-components -p packages_update repository
and launch the maintenance tool in your installation.

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<Installer>
<Name>Repository category Installer Example</Name>
<Version>1.0.0</Version>
<Title>Repository category Installer Example</Title>
<Publisher>The Qt Company</Publisher>
<!-- Directory name is used in component.xml -->
<StartMenuDir>Qt IFW Examples</StartMenuDir>
<TargetDir>@HomeDir@/IfwExamples/repositoryCategories</TargetDir>
<RemoteRepositories>
<Repository>
<Url>http://localhost/repository</Url>
</Repository>
</RemoteRepositories>
<RepositoryCategories>
<RepositoryCategoryDisplayname>Releases</RepositoryCategoryDisplayname>
<RemoteRepositories>
<DisplayName>Category 1</DisplayName>
<Repository>
<Url>http://localhost/repository1</Url>
</Repository>
<Repository>
<Url>http://localhost/repository2</Url>
</Repository>
</RemoteRepositories>
<RemoteRepositories>
<DisplayName>Category 2</DisplayName>
<Repository>
<Url>http://localhost/repository3</Url>
</Repository>
</RemoteRepositories>
</RepositoryCategories>
</Installer>

View File

@ -0,0 +1,2 @@
Example content for package A.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>A</DisplayName>
<Description>Example component A</Description>
<Version>1.0.2-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package B.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>B</DisplayName>
<Description>Example component B</Description>
<Version>1.0.0-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package A2, using category 1.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>A2 (from category 1)</DisplayName>
<Description>Example component A2</Description>
<Version>1.0.3-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package B2, using category 1.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>B2 (from category 1)</DisplayName>
<Description>Example component B</Description>
<Version>1.0.0-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package A, using category 1.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>A (from category 1)</DisplayName>
<Description>Example component A</Description>
<Version>1.0.3-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package B, using category 1.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>B (from category 1)</DisplayName>
<Description>Example component B</Description>
<Version>1.0.0-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package A, using category 2.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>A (from category 2)</DisplayName>
<Description>Example component A</Description>
<Version>1.0.3-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,2 @@
Example content for package B, using category 2.

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<DisplayName>B (from category 2)</DisplayName>
<Description>Example component B</Description>
<Version>1.0.0-1</Version>
<ReleaseDate>2015-01-01</ReleaseDate>
<Default>true</Default>
</Package>

View File

@ -0,0 +1,13 @@
TEMPLATE = aux
INSTALLER = installer
INPUT = $$PWD/config/config.xml $$PWD/packages
example.input = INPUT
example.output = $$INSTALLER
example.commands = ../../bin/binarycreator --online-only -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT}
example.CONFIG += target_predeps no_link combine
QMAKE_EXTRA_COMPILERS += example
OTHER_FILES = README

View File

@ -91,6 +91,7 @@ static const QLatin1String scAllUsers("AllUsers");
static const QLatin1String scSupportsModify("SupportsModify");
static const QLatin1String scAllowUnstableComponents("AllowUnstableComponents");
static const QLatin1String scSaveDefaultRepositories("SaveDefaultRepositories");
static const QLatin1String scRepositoryCategoryDisplayName("RepositoryCategoryDisplayName");
const char scRelocatable[] = "@RELOCATABLE_PATH@";

View File

@ -131,7 +131,8 @@ HEADERS += packagemanagercore.h \
lib7z_guid.h \
lib7z_create.h \
lib7z_extract.h \
lib7z_list.h
lib7z_list.h \
repositorycategory.h
SOURCES += packagemanagercore.cpp \
packagemanagercore_p.cpp \
@ -206,7 +207,8 @@ SOURCES += packagemanagercore.cpp \
serverauthenticationdialog.cpp \
keepaliveobject.cpp \
systeminfo.cpp \
packagesource.cpp
packagesource.cpp \
repositorycategory.cpp
FORMS += proxycredentialsdialog.ui \
serverauthenticationdialog.ui

View File

@ -1,6 +1,6 @@
/**************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@ -70,11 +70,33 @@ MetadataJob::~MetadataJob()
reset();
}
Repository MetadataJob::repositoryForDirectory(const QString &directory) const
/*
* Parse the metadata of currently selected repositories. We cannot
* return all metadata as that contains metadata also from categorized archived
* repositories which might not be currently selected.
*/
QList<Metadata> MetadataJob::metadata() const
{
return m_metadata.value(directory).repository;
QList<Metadata> metadata = m_metaFromDefaultRepositories.values();
foreach (RepositoryCategory repositoryCategory, m_core->settings().repositoryCategories()) {
if (m_core->isUpdater() || (repositoryCategory.isEnabled() && m_fetchedArchive.contains(repositoryCategory.displayname()))) {
QList<ArchiveMetadata> archiveMetaList = m_fetchedArchive.values(repositoryCategory.displayname());
foreach (ArchiveMetadata archiveMeta, archiveMetaList) {
metadata.append(archiveMeta.metaData);
}
}
}
return metadata;
}
Repository MetadataJob::repositoryForDirectory(const QString &directory) const
{
if (m_metaFromDefaultRepositories.contains(directory))
return m_metaFromDefaultRepositories.value(directory).repository;
else
return m_metaFromArchive.value(directory).repository;
}
// -- private slots
@ -86,13 +108,12 @@ void MetadataJob::doStart()
}
const ProductKeyCheck *const productKeyCheck = ProductKeyCheck::instance();
if (!m_addCompressedPackages) {
reset();
emit infoMessage(this, tr("Preparing meta information download..."));
const bool onlineInstaller = m_core->isInstaller() && !m_core->isOfflineOnly();
if (onlineInstaller || m_core->isMaintainer()) {
QList<FileTaskItem> items;
foreach (const Repository &repo, m_core->settings().repositories()) {
QSet<Repository> repositories = getRepositories();
foreach (const Repository &repo, repositories) {
if (repo.isEnabled() &&
productKeyCheck->isValidRepository(repo)) {
QAuthenticator authenticator;
@ -451,7 +472,9 @@ bool MetadataJob::fetchMetaDataPackages()
void MetadataJob::reset()
{
m_packages.clear();
m_metadata.clear();
m_metaFromDefaultRepositories.clear();
m_metaFromArchive.clear();
m_fetchedArchive.clear();
setError(Job::NoError);
setErrorString(QString());
@ -587,7 +610,17 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
}
}
}
m_metadata.insert(metadata.directory, metadata);
if (metadata.repository.archivename().isEmpty()) {
m_metaFromDefaultRepositories.insert(metadata.directory, metadata);
} else {
//Hash metadata to help checking if meta for repository is already fetched
ArchiveMetadata archiveMetadata;
archiveMetadata.metaData = metadata;
m_fetchedArchive.insertMulti(metadata.repository.archivename(), archiveMetadata);
// Hash for faster lookups
m_metaFromArchive.insert(metadata.directory, metadata);
}
// search for additional repositories that we might need to check
const QDomNode repositoryUpdate = root.firstChildElement(QLatin1String("RepositoryUpdate"));
@ -670,8 +703,31 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList<FileTaskResult> &re
}
double taskCount = m_packages.length()/static_cast<double>(m_downloadableChunkSize);
m_totalTaskCount = qCeil(taskCount);
m_taskNumber = 0;
return XmlDownloadSuccess;
}
QSet<Repository> MetadataJob::getRepositories()
{
QSet<Repository> repositories;
//In the first run, m_metadata is empty. Get always the default repositories
if (m_metaFromDefaultRepositories.isEmpty()) {
repositories = m_core->settings().repositories();
}
// Fetch repositories under archive which are selected in UI.
// If repository is already fetched, do not fetch it again.
// In updater mode, fetch always all archive repositories to get updates
foreach (RepositoryCategory repositoryCategory, m_core->settings().repositoryCategories()) {
if (m_core->isUpdater() || (repositoryCategory.isEnabled() && !m_fetchedArchive.contains(repositoryCategory.displayname()))) {
foreach (Repository repository, repositoryCategory.repositories()) {
repositories.insert(repository);
}
}
}
return repositories;
}
} // namespace QInstaller

View File

@ -1,6 +1,6 @@
/**************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@ -46,6 +46,12 @@ struct Metadata
Repository repository;
};
struct ArchiveMetadata
{
QString archive;
Metadata metaData;
};
class INSTALLER_EXPORT MetadataJob : public Job
{
Q_OBJECT
@ -61,7 +67,7 @@ public:
explicit MetadataJob(QObject *parent = 0);
~MetadataJob();
QList<Metadata> metadata() const { return m_metadata.values(); }
QList<Metadata> metadata() const;
Repository repositoryForDirectory(const QString &directory) const;
void setPackageManagerCore(PackageManagerCore *core) { m_core = core; }
void addCompressedPackages(bool addCompressPackage) { m_addCompressedPackages = addCompressPackage;}
@ -85,13 +91,13 @@ private:
void reset();
void resetCompressedFetch();
Status parseUpdatesXml(const QList<FileTaskResult> &results);
QSet<Repository> getRepositories();
private:
PackageManagerCore *m_core;
QList<FileTaskItem> m_packages;
TempDirDeleter m_tempDirDeleter;
QHash<QString, Metadata> m_metadata;
QFutureWatcher<FileTaskResult> m_xmlTask;
QFutureWatcher<FileTaskResult> m_metadataTask;
QHash<QFutureWatcher<void> *, QObject*> m_unzipTasks;
@ -103,6 +109,9 @@ private:
int m_taskNumber;
int m_totalTaskCount;
QStringList m_shaMissmatchPackages;
QHash<QString, ArchiveMetadata> m_fetchedArchive;
QHash<QString, Metadata> m_metaFromDefaultRepositories;
QHash<QString, Metadata> m_metaFromArchive; //for faster lookups.
};
} // namespace QInstaller

View File

@ -1176,7 +1176,6 @@ bool PackageManagerCore::fetchCompressedPackagesTree()
return fetchPackagesTree(packages, installedPackages);
}
/*!
Checks for packages to install. Returns \c true if newer versions exist
and they can be installed.

View File

@ -2210,14 +2210,12 @@ LocalPackagesHash PackageManagerCorePrivate::localInstalledPackages()
bool PackageManagerCorePrivate::fetchMetaInformationFromRepositories()
{
if (m_repoFetched)
return m_repoFetched;
m_updates = false;
m_repoFetched = false;
m_updateSourcesAdded = false;
try {
m_metadataJob.addCompressedPackages(false);
m_metadataJob.start();
m_metadataJob.waitForFinished();
} catch (Error &error) {

View File

@ -39,6 +39,7 @@
#include "utils.h"
#include "scriptengine.h"
#include "productkeycheck.h"
#include "repositorycategory.h"
#include "sysinfo.h"
@ -71,6 +72,7 @@
#include <QVBoxLayout>
#include <QShowEvent>
#include <QFileDialog>
#include <QGroupBox>
#ifdef Q_OS_WIN
# include <qt_windows.h>
@ -1863,6 +1865,8 @@ public:
, m_updaterModel(m_core->updaterComponentModel())
, m_currentModel(m_allModel)
, m_compressedButtonVisible(false)
, m_allowCompressedRepositoryInstall(false)
, m_archiveButtonVisible(false)
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
@ -1871,32 +1875,42 @@ public:
connect(m_updaterModel, SIGNAL(checkStateChanged(QInstaller::ComponentModel::ModelState)),
this, SLOT(onModelStateChanged(QInstaller::ComponentModel::ModelState)));
QHBoxLayout *hlayout = new QHBoxLayout;
hlayout->addWidget(m_treeView, 3);
m_descriptionVLayout = new QVBoxLayout;
m_descriptionVLayout->setObjectName(QLatin1String("DescriptionLayout"));
m_descriptionLabel = new QLabel(q);
m_descriptionLabel->setWordWrap(true);
m_descriptionLabel->setObjectName(QLatin1String("ComponentDescriptionLabel"));
m_vlayout = new QVBoxLayout;
m_vlayout->setObjectName(QLatin1String("VerticalLayout"));
m_vlayout->addWidget(m_descriptionLabel);
m_descriptionVLayout->addWidget(m_descriptionLabel);
m_sizeLabel = new QLabel(q);
m_sizeLabel->setWordWrap(true);
m_vlayout->addWidget(m_sizeLabel);
m_sizeLabel->setObjectName(QLatin1String("ComponentSizeLabel"));
#ifdef INSTALLCOMPRESSED
allowCompressedRepositoryInstall();
#endif
m_vlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
m_descriptionVLayout->addWidget(m_sizeLabel);
m_descriptionVLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding));
hlayout->addLayout(m_vlayout, 2);
QVBoxLayout *layout = new QVBoxLayout(q);
layout->addLayout(hlayout, 1);
m_mainHLayout = new QHBoxLayout;
m_treeViewVLayout = new QVBoxLayout;
m_treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
m_bspLabel = new QLabel();
m_bspLabel->hide();
m_treeViewVLayout->addWidget(m_bspLabel);
m_progressBar = new QProgressBar();
m_progressBar->setRange(0, 0);
m_progressBar->hide();
m_progressBar->setObjectName(QLatin1String("CompressedInstallProgressBar"));
m_treeViewVLayout->addWidget(m_progressBar);
connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int)));
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
connect(m_core, &PackageManagerCore::metaJobTotalProgress, this,
&ComponentSelectionPage::Private::setTotalProgress);
m_buttonHLayout = new QHBoxLayout;
m_checkDefault = new QPushButton;
connect(m_checkDefault, &QAbstractButton::clicked,
this, &ComponentSelectionPage::Private::selectDefault);
@ -1912,64 +1926,95 @@ public:
"reset to already installed components")));
m_checkDefault->setText(ComponentSelectionPage::tr("&Reset"));
}
hlayout = new QHBoxLayout;
hlayout->addWidget(m_checkDefault);
m_buttonHLayout->addWidget(m_checkDefault);
m_checkAll = new QPushButton;
hlayout->addWidget(m_checkAll);
connect(m_checkAll, &QAbstractButton::clicked,
this, &ComponentSelectionPage::Private::selectAll);
m_checkAll->setObjectName(QLatin1String("SelectAllComponentsButton"));
m_checkAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+S",
"select all components")));
m_checkAll->setText(ComponentSelectionPage::tr("&Select All"));
m_buttonHLayout->addWidget(m_checkAll);
m_uncheckAll = new QPushButton;
hlayout->addWidget(m_uncheckAll);
connect(m_uncheckAll, &QAbstractButton::clicked,
this, &ComponentSelectionPage::Private::deselectAll);
m_uncheckAll->setObjectName(QLatin1String("DeselectAllComponentsButton"));
m_uncheckAll->setShortcut(QKeySequence(ComponentSelectionPage::tr("Alt+D",
"deselect all components")));
m_uncheckAll->setText(ComponentSelectionPage::tr("&Deselect All"));
m_buttonHLayout->addWidget(m_uncheckAll);
hlayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::MinimumExpanding,
QSizePolicy::MinimumExpanding));
layout->addLayout(hlayout);
m_treeViewVLayout->addLayout(m_buttonHLayout);
m_treeViewVLayout->addWidget(m_treeView, 3);
m_mainHLayout->addLayout(m_treeViewVLayout, 3);
m_mainHLayout->addLayout(m_descriptionVLayout, 2);
QVBoxLayout *layout = new QVBoxLayout(q);
layout->addLayout(m_mainHLayout, 1);
#ifdef INSTALLCOMPRESSED
allowCompressedRepositoryInstall();
#endif
}
void allowCompressedRepositoryInstall()
{
if (m_compressedButtonVisible) {
m_allowCompressedRepositoryInstall = true;
}
void showCompressedRepositoryButton()
{
if (m_compressedButtonVisible || !m_allowCompressedRepositoryInstall) {
return;
}
connect(m_core, SIGNAL(metaJobProgress(int)), this, SLOT(onProgressChanged(int)));
connect(m_core, SIGNAL(metaJobInfoMessage(QString)), this, SLOT(setMessage(QString)));
m_bspLabel = new QLabel(ComponentSelectionPage::tr("To install new "\
"compressed repository, browse the repositories from your computer"),q);
m_bspLabel->setWordWrap(true);
m_bspLabel->setObjectName(QLatin1String("CompressedButtonLabel"));
m_vlayout->addSpacing(50);
m_vlayout->addWidget(m_bspLabel);
m_progressBar = new QProgressBar();
m_progressBar->setRange(0, 0);
m_progressBar->hide();
m_vlayout->addWidget(m_progressBar);
m_progressBar->setObjectName(QLatin1String("CompressedInstallProgressBar"));
m_installCompressButton = new QPushButton;
connect(m_installCompressButton, &QAbstractButton::clicked,
this, &ComponentSelectionPage::Private::selectCompressedPackage);
m_installCompressButton->setObjectName(QLatin1String("InstallCompressedPackageButton"));
m_installCompressButton->setText(ComponentSelectionPage::tr("&Browse QBSP files"));
m_vlayout->addWidget(m_installCompressButton);
QWizard *wizard = qobject_cast<QWizard*>(m_core->guiObject());
if (wizard) {
wizard->setOption(QWizard::HaveCustomButton2, true);
wizard->setButtonText(QWizard::CustomButton2,
ComponentSelectionPage::tr("&Browse QBSP files"));
connect(wizard, &QWizard::customButtonClicked,
this, &ComponentSelectionPage::Private::selectCompressedPackage);
q->gui()->updateButtonLayout();
}
m_compressedButtonVisible = true;
}
void setupArchiveButton()
{
if (m_archiveButtonVisible)
return;
QVBoxLayout *vLayout = new QVBoxLayout;
m_archiveVLayout = new QVBoxLayout;
m_archiveGroupBox = new QGroupBox(q);
m_archiveGroupBox->setTitle(m_core->settings().repositoryCategoryDisplayName());
QVBoxLayout *groupboxLayout = new QVBoxLayout(m_archiveGroupBox);
m_fetchArchiveButton = new QPushButton(tr("Fetch"));
connect(m_fetchArchiveButton, &QPushButton::clicked, this,
&ComponentSelectionPage::Private::fetchRepositoryCategories);
foreach (RepositoryCategory repository, m_core->settings().repositoryCategories()) {
QCheckBox *checkBox = new QCheckBox;
connect(checkBox, &QCheckBox::stateChanged, this,
&ComponentSelectionPage::Private::checkboxStateChanged);
checkBox->setText(repository.displayname());
groupboxLayout->addWidget(checkBox);
}
m_archiveVLayout->insertWidget(0, m_archiveGroupBox);
m_metadataProgressLabel = new QLabel(q);
m_archiveVLayout->addWidget(m_metadataProgressLabel);
vLayout->addWidget(m_archiveGroupBox);
vLayout->addWidget(m_fetchArchiveButton);
vLayout->addStretch();
m_mainHLayout->insertLayout(0, vLayout);
m_archiveButtonVisible = true;
}
void updateTreeView()
{
m_checkDefault->setVisible(m_core->isInstaller() || m_core->isPackageManager());
@ -2061,6 +2106,77 @@ public slots:
m_currentModel->setCheckedState(ComponentModel::AllUnchecked);
}
void checkboxStateChanged()
{
QList<QCheckBox*> checkboxes = m_archiveGroupBox->findChildren<QCheckBox *>();
bool enableFetchButton = false;
foreach (QCheckBox *checkbox, checkboxes) {
if (checkbox->isChecked()) {
enableFetchButton = true;
break;
}
}
}
void enableArchiveRepos(int index, bool enable) {
RepositoryCategory archiveRepo = m_core->settings().repositoryCategories().toList().at(index);
RepositoryCategory replacement = archiveRepo;
replacement.setEnabled(enable);
QSet<RepositoryCategory> tmpArchiveRepos = m_core->settings().repositoryCategories();
if (tmpArchiveRepos.contains(archiveRepo)) {
tmpArchiveRepos.remove(archiveRepo);
tmpArchiveRepos.insert(replacement);
m_core->settings().addRepositoryCategories(tmpArchiveRepos);
}
}
void updateWidgetVisibility(bool show)
{
if (show) {
QSpacerItem *verticalSpacer2 = new QSpacerItem(0, 0, QSizePolicy::Minimum,
QSizePolicy::Expanding);
m_treeViewVLayout->addSpacerItem(verticalSpacer2);
} else {
QSpacerItem *item = m_treeViewVLayout->spacerItem();
m_treeViewVLayout->removeItem(item);
}
m_fetchArchiveButton->setDisabled(show);
m_progressBar->setVisible(show);
m_bspLabel->setVisible(show);
m_archiveGroupBox->setDisabled(show);
m_treeView->setVisible(!show);
m_checkDefault->setVisible(!show);
m_checkAll->setVisible(!show);
m_uncheckAll->setVisible(!show);
m_descriptionLabel->setVisible(!show);
QPushButton *const b = qobject_cast<QPushButton *>(q->gui()->button(QWizard::NextButton));
b->setEnabled(!show);
if (QAbstractButton *bspButton = q->gui()->button(QWizard::CustomButton2))
bspButton->setEnabled(!show);
}
void fetchRepositoryCategories()
{
updateWidgetVisibility(true);
QCheckBox *checkbox;
QList<QCheckBox*> checkboxes = m_archiveGroupBox->findChildren<QCheckBox *>();
for (int i = 0; i < checkboxes.count(); i++) {
checkbox = checkboxes.at(i);
enableArchiveRepos(i, checkbox->isChecked());
}
if (!m_core->fetchRemotePackagesTree()) {
m_metadataProgressLabel->setText(m_core->error());
} else {
updateTreeView();
m_metadataProgressLabel->setText(QLatin1String());
}
updateWidgetVisibility(false);
}
void selectCompressedPackage()
{
QString defaultDownloadDirectory =
@ -2076,10 +2192,7 @@ public slots:
set.insert(repository);
}
if (set.count() > 0) {
m_progressBar->show();
m_installCompressButton->hide();
QPushButton *const b = qobject_cast<QPushButton *>(q->gui()->button(QWizard::NextButton));
b->setEnabled(false);
updateWidgetVisibility(true);
m_core->settings().addTemporaryRepositories(set, false);
if (!m_core->fetchCompressedPackagesTree()) {
setMessage(m_core->error());
@ -2089,11 +2202,8 @@ public slots:
setMessage(ComponentSelectionPage::tr("To install new "\
"compressed repository, browse the repositories from your computer"));
}
m_progressBar->hide();
m_installCompressButton->show();
b->setEnabled(true);
}
updateWidgetVisibility(false);
}
/*!
@ -2114,6 +2224,12 @@ public slots:
m_bspLabel->setText(msg);
}
void setTotalProgress(int totalProgress)
{
if (m_progressBar)
m_progressBar->setRange(0, totalProgress);
}
void selectDefault()
{
m_currentModel->setCheckedState(ComponentModel::DefaultChecked);
@ -2152,10 +2268,19 @@ public:
QPushButton *m_uncheckAll;
QPushButton *m_checkDefault;
QPushButton *m_installCompressButton;
QGroupBox *m_archiveGroupBox;
QPushButton *m_fetchArchiveButton;
QLabel *m_bspLabel;
QLabel *m_metadataProgressLabel;
QProgressBar *m_progressBar;
QVBoxLayout *m_vlayout;
QVBoxLayout *m_descriptionVLayout;
QHBoxLayout *m_mainHLayout;
QVBoxLayout *m_treeViewVLayout;
QVBoxLayout *m_archiveVLayout;
QHBoxLayout *m_buttonHLayout;
bool m_compressedButtonVisible;
bool m_allowCompressedRepositoryInstall;
bool m_archiveButtonVisible;
};
@ -2211,6 +2336,21 @@ void ComponentSelectionPage::entering()
d->updateTreeView();
setModified(isComplete());
if (core->settings().repositoryCategories().count() > 0 && !core->isOfflineOnly()
&& !core->isUpdater()) {
d->setupArchiveButton();
}
d->showCompressedRepositoryButton();
}
void ComponentSelectionPage::leaving()
{
QWizard *wizard = qobject_cast<QWizard*>(d->m_core->guiObject());
if (wizard && (gui()->options() & QWizard::HaveCustomButton2)) {
wizard->setOption(QWizard::HaveCustomButton2, false);
gui()->updateButtonLayout();
d->m_compressedButtonVisible = false;
}
}
/*!

View File

@ -314,6 +314,7 @@ public:
protected:
void entering();
void leaving();
void showEvent(QShowEvent *event);
private Q_SLOTS:

View File

@ -57,6 +57,7 @@ Repository::Repository(const Repository &other)
, m_password(other.m_password)
, m_displayname(other.m_displayname)
, m_compressed(other.m_compressed)
, m_archivename(other.m_archivename)
{
registerMetaType();
}
@ -183,7 +184,7 @@ void Repository::setPassword(const QString &password)
}
/*!
Returns the Name for the repository to be displayed instead of the URL
Returns the Name for the repository to be displayed instead of the URL.
*/
QString Repository::displayname() const
{
@ -198,6 +199,22 @@ void Repository::setDisplayName(const QString &displayname)
m_displayname = displayname;
}
/*!
Returns the archive name if the repository belongs to an archive.
*/
QString Repository::archivename() const
{
return m_archivename;
}
/*!
Sets the archive name to \a archivename if the repository belongs to an archive.
*/
void Repository::setArchiveName(const QString &archivename)
{
m_archivename = archivename;
}
/*!
Returns true if repository is compressed
*/
@ -248,6 +265,7 @@ const Repository &Repository::operator=(const Repository &other)
m_password = other.m_password;
m_displayname = other.m_displayname;
m_compressed = other.m_compressed;
m_archivename = other.m_archivename;
return *this;
}
@ -273,7 +291,7 @@ QDataStream &operator<<(QDataStream &ostream, const Repository &repository)
{
return ostream << repository.m_url.toEncoded().toBase64() << repository.m_default << repository.m_enabled
<< repository.m_username.toUtf8().toBase64() << repository.m_password.toUtf8().toBase64()
<< repository.m_displayname.toUtf8().toBase64();
<< repository.m_displayname.toUtf8().toBase64() << repository.m_archivename.toUtf8().toBase64();
}
}

View File

@ -64,6 +64,9 @@ public:
QString displayname() const;
void setDisplayName(const QString &displayname);
QString archivename() const;
void setArchiveName(const QString &archivename);
bool isCompressed() const;
void setCompressed(bool compressed);
bool operator==(const Repository &other) const;
@ -82,6 +85,7 @@ private:
QString m_username;
QString m_password;
QString m_displayname;
QString m_archivename;
bool m_compressed;
};

View File

@ -0,0 +1,159 @@
/**************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
**************************************************************************/
#include "repositorycategory.h"
#include "filedownloaderfactory.h"
#include <QDataStream>
#include <QFileInfo>
#include <QStringList>
namespace QInstaller {
template <typename T>
static QSet<T> variantListToSet(const QVariantList &list)
{
QSet<T> set;
foreach (const QVariant &variant, list)
set.insert(variant.value<T>());
return set;
}
/*!
Constructs an uninitialized RepositoryCategory object.
*/
RepositoryCategory::RepositoryCategory()
: m_enabled(false)
{
registerMetaType();
}
/*!
Constructs a new category by using all fields of the given category \a other.
*/
RepositoryCategory::RepositoryCategory(const RepositoryCategory &other)
: m_displayname(other.m_displayname), m_data(other.m_data), m_enabled(other.m_enabled)
{
registerMetaType();
}
void RepositoryCategory::registerMetaType()
{
qRegisterMetaType<RepositoryCategory>("RepositoryCategory");
qRegisterMetaTypeStreamOperators<RepositoryCategory>("RepositoryCategory");
}
/*!
Returns the Name for the category to be displayed.
*/
QString RepositoryCategory::displayname() const
{
return m_displayname;
}
/*!
Sets the DisplayName of the category to \a displayname.
*/
void RepositoryCategory::setDisplayName(const QString &displayname)
{
m_displayname = displayname;
}
/*!
Returns the list of repositories the category has.
*/
QSet<Repository> RepositoryCategory::repositories() const
{
return variantListToSet<Repository>(m_data.values(QLatin1String("Repositories")));
}
/*!
Inserts a set of \a repositories to the category.
*/
void RepositoryCategory::setRepositories(const QSet<Repository> repositories)
{
foreach (const Repository &repository, repositories)
m_data.insertMulti(QLatin1String("Repositories"), QVariant().fromValue(repository));
}
/*!
Inserts \a repository to the category.
*/
void RepositoryCategory::addRepository(const Repository repository)
{
m_data.insertMulti(QLatin1String("Repositories"), QVariant().fromValue(repository));
}
/*!
Returns whether this category is enabled and used during information retrieval.
*/
bool RepositoryCategory::isEnabled() const
{
return m_enabled;
}
/*!
Sets this category to \a enabled state and and thus determines whether to use this
repository for information retrieval.
*/
void RepositoryCategory::setEnabled(bool enabled)
{
m_enabled = enabled;
}
/*!
Compares the values of this category to \a other and returns true if they are equal.
*/
bool RepositoryCategory::operator==(const RepositoryCategory &other) const
{
return m_displayname == other.m_displayname;
}
/*!
Returns true if the \a other category is not equal to this repository; otherwise returns false. Two
categories are considered equal if they contain the same displayname. \sa operator==()
*/
bool RepositoryCategory::operator!=(const RepositoryCategory &other) const
{
return !(*this == other);
}
QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository)
{
return istream;
}
QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository)
{
return ostream << repository.m_displayname.toUtf8().toBase64() << repository.m_data;
}
}

View File

@ -0,0 +1,86 @@
/**************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
**************************************************************************/
#ifndef ARCHIVEREPOSITORY_H
#define ARCHIVEREPOSITORY_H
#include "installer_global.h"
#include "repository.h"
#include <QtCore/QMetaType>
#include <QtCore/QUrl>
#include <QSet>
namespace QInstaller {
class INSTALLER_EXPORT RepositoryCategory
{
public:
explicit RepositoryCategory();
RepositoryCategory(const RepositoryCategory &other);
static void registerMetaType();
QString displayname() const;
void setDisplayName(const QString &displayname);
QSet<Repository> repositories() const;
void setRepositories(const QSet<Repository> repositories);
void addRepository(const Repository repository);
bool isEnabled() const;
void setEnabled(bool enabled);
bool operator==(const RepositoryCategory &other) const;
bool operator!=(const RepositoryCategory &other) const;
uint qHash(const RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository);
friend INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository);
private:
QVariantHash m_data;
QString m_displayname;
bool m_enabled;
};
inline uint qHash(const RepositoryCategory &repository)
{
return qHash(repository.displayname());
}
INSTALLER_EXPORT QDataStream &operator>>(QDataStream &istream, RepositoryCategory &repository);
INSTALLER_EXPORT QDataStream &operator<<(QDataStream &ostream, const RepositoryCategory &repository);
} // namespace QInstaller
Q_DECLARE_METATYPE(QInstaller::RepositoryCategory)
#endif // ARCHIVEREPOSITORY_H

View File

@ -30,6 +30,7 @@
#include "errors.h"
#include "qinstallerglobal.h"
#include "repository.h"
#include "repositorycategory.h"
#include <QtCore/QFileInfo>
#include <QtCore/QStringList>
@ -55,6 +56,7 @@ static const QLatin1String scUserRepositories("UserRepositories");
static const QLatin1String scTmpRepositories("TemporaryRepositories");
static const QLatin1String scMaintenanceToolIniFile("MaintenanceToolIniFile");
static const QLatin1String scRemoteRepositories("RemoteRepositories");
static const QLatin1String scRepositoryCategories("RepositoryCategories");
static const QLatin1String scDependsOnLocalInstallerBinary("DependsOnLocalInstallerBinary");
static const QLatin1String scTranslations("Translations");
static const QLatin1String scCreateLocalRepository("CreateLocalRepository");
@ -133,11 +135,15 @@ static QStringList readArgumentAttributes(QXmlStreamReader &reader, Settings::Pa
return arguments;
}
static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefault, Settings::ParseMode parseMode)
static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefault, Settings::ParseMode parseMode, QString *displayName = 0)
{
qDebug()<<__FUNCTION__;
QSet<Repository> set;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Repository")) {
if (reader.name() == QLatin1String("DisplayName")) {
//remote repository can have also displayname. Needed when creating archive repositories
*displayName = reader.readElementText();
} else if (reader.name() == QLatin1String("Repository")) {
Repository repo(QString(), isDefault);
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("Url")) {
@ -160,6 +166,8 @@ static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefaul
.arg(reader.name().toString()), parseMode);
}
}
if (displayName && !displayName->isEmpty())
repo.setArchiveName(*displayName);
set.insert(repo);
} else {
raiseError(reader, QString::fromLatin1("Unexpected element \"%1\".").arg(reader.name().toString()),
@ -174,6 +182,23 @@ static QSet<Repository> readRepositories(QXmlStreamReader &reader, bool isDefaul
return set;
}
static QSet<RepositoryCategory> readRepositoryCategories(QXmlStreamReader &reader, bool isDefault, Settings::ParseMode parseMode,
QString *repositoryCategoryName)
{
QSet<RepositoryCategory> archiveSet;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("RemoteRepositories")) {
RepositoryCategory archiveRepo;
QString displayName;
archiveRepo.setRepositories(readRepositories(reader, isDefault, parseMode, &displayName));
archiveRepo.setDisplayName(displayName);
archiveSet.insert(archiveRepo);
} else if (reader.name() == QLatin1String("RepositoryCategoryDisplayname")) {
*repositoryCategoryName = reader.readElementText();
}
}
return archiveSet;
}
// -- Settings::Private
@ -257,7 +282,7 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
<< scRepositorySettingsPageVisible << scTargetConfigurationFile
<< scRemoteRepositories << scTranslations << scUrlQueryString << QLatin1String(scControlScript)
<< scCreateLocalRepository << scInstallActionColumnVisible << scSupportsModify << scAllowUnstableComponents
<< scSaveDefaultRepositories;
<< scSaveDefaultRepositories << scRepositoryCategories;
Settings s;
s.d->m_data.insert(scPrefix, prefix);
@ -280,11 +305,16 @@ Settings Settings::fromFileAndPrefix(const QString &path, const QString &prefix,
s.setRunProgramArguments(readArgumentAttributes(reader, parseMode, QLatin1String("Argument")));
} else if (name == scRemoteRepositories) {
s.addDefaultRepositories(readRepositories(reader, true, parseMode));
} else if (name == scRepositoryCategories) {
QString repositoryCategoryName;
s.addRepositoryCategories(readRepositoryCategories(reader, true, parseMode, &repositoryCategoryName));
if (!repositoryCategoryName.isEmpty()) {
s.setRepositoryCategoryDisplayName(repositoryCategoryName);
}
} else {
s.d->m_data.insert(name, reader.readElementText(QXmlStreamReader::SkipChildElements));
}
}
if (reader.error() != QXmlStreamReader::NoError) {
throw Error(QString::fromLatin1("Error in %1, line %2, column %3: %4").arg(path).arg(reader
.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
@ -549,6 +579,11 @@ QSet<Repository> Settings::defaultRepositories() const
return variantListToSet<Repository>(d->m_data.values(scRepositories));
}
QSet<RepositoryCategory> Settings::repositoryCategories() const
{
return variantListToSet<RepositoryCategory>(d->m_data.values(scRepositoryCategories));
}
void Settings::setDefaultRepositories(const QSet<Repository> &repositories)
{
d->m_data.remove(scRepositories);
@ -561,6 +596,12 @@ void Settings::addDefaultRepositories(const QSet<Repository> &repositories)
d->m_data.insertMulti(scRepositories, QVariant().fromValue(repository));
}
void Settings::addRepositoryCategories(const QSet<RepositoryCategory> &repositories)
{
foreach (const RepositoryCategory &repository, repositories)
d->m_data.insertMulti(scRepositoryCategories, QVariant().fromValue(repository));
}
static bool apply(const RepoHash &updates, QHash<QUrl, Repository> *reposToUpdate)
{
bool update = false;
@ -767,3 +808,14 @@ void Settings::setSaveDefaultRepositories(bool save)
{
d->m_data.insert(scSaveDefaultRepositories, save);
}
QString Settings::repositoryCategoryDisplayName() const
{
QString displayName = d->m_data.value(QLatin1String(scRepositoryCategoryDisplayName)).toString();
return displayName.isEmpty() ? tr("Package categories") : displayName;
}
void Settings::setRepositoryCategoryDisplayName(const QString& name)
{
d->m_data.insert(scRepositoryCategoryDisplayName, name);
}

View File

@ -31,6 +31,7 @@
#include "constants.h"
#include "installer_global.h"
#include "repositorycategory.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QSharedDataPointer>
@ -114,8 +115,10 @@ public:
QSet<Repository> repositories() const;
QSet<Repository> defaultRepositories() const;
QSet<RepositoryCategory> repositoryCategories() const;
void setDefaultRepositories(const QSet<Repository> &repositories);
void addDefaultRepositories(const QSet<Repository> &repositories);
void addRepositoryCategories(const QSet<RepositoryCategory> &repositories);
Settings::Update updateDefaultRepositories(const RepoHash &updates);
QSet<Repository> temporaryRepositories() const;
@ -160,6 +163,9 @@ public:
bool saveDefaultRepositories() const;
void setSaveDefaultRepositories(bool save);
QString repositoryCategoryDisplayName() const;
void setRepositoryCategoryDisplayName(const QString &displayName);
private:
class Private;
QSharedDataPointer<Private> d;