summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKatja Marttila <katja.marttila@qt.io>2018-03-09 10:21:54 +0200
committerKatja Marttila <katja.marttila@qt.io>2018-09-26 04:46:21 +0000
commit0f8d11ca8e8312fc0d0dc56e0c2a1154ff3de77c (patch)
treea0f00dbacd2c03ee3013e648464e2df61c7d1b8b
parentb3d140a2c899219a02c1b31efa4e36723eb71c07 (diff)
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>
-rw-r--r--doc/images/ifw-repository-categories.pngbin0 -> 58532 bytes
-rw-r--r--doc/includes/installerfw-examples-generating-online.qdocinc18
-rw-r--r--doc/installerfw.qdoc34
-rw-r--r--examples/doc/images/qtifw-examples-repository-categories.pngbin0 -> 58532 bytes
-rw-r--r--examples/doc/repositorycategories.qdoc117
-rw-r--r--examples/examples.pro1
-rw-r--r--examples/repositorycategories/README30
-rw-r--r--examples/repositorycategories/config/config.xml33
-rw-r--r--examples/repositorycategories/packages/A/data/A.txt2
-rw-r--r--examples/repositorycategories/packages/A/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages/B/data/B.txt2
-rw-r--r--examples/repositorycategories/packages/B/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages2_forcategory1/A2Cagetory1/data/A2_category1.txt2
-rw-r--r--examples/repositorycategories/packages2_forcategory1/A2Cagetory1/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages2_forcategory1/B2Category1/data/B2_category1.txt2
-rw-r--r--examples/repositorycategories/packages2_forcategory1/B2Category1/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages_forcategory1/ACagetory1/data/A_category1.txt2
-rw-r--r--examples/repositorycategories/packages_forcategory1/ACagetory1/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages_forcategory1/BCategory1/data/B_category1.txt2
-rw-r--r--examples/repositorycategories/packages_forcategory1/BCategory1/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages_forcategory2/ACategory2/data/A_category2.txt2
-rw-r--r--examples/repositorycategories/packages_forcategory2/ACategory2/meta/package.xml8
-rw-r--r--examples/repositorycategories/packages_forcategory2/BCategory2/data/B_category2.txt2
-rw-r--r--examples/repositorycategories/packages_forcategory2/BCategory2/meta/package.xml8
-rw-r--r--examples/repositorycategories/repositorycategories.pro13
-rw-r--r--src/libs/installer/constants.h1
-rw-r--r--src/libs/installer/installer.pro6
-rw-r--r--src/libs/installer/metadatajob.cpp72
-rw-r--r--src/libs/installer/metadatajob.h15
-rw-r--r--src/libs/installer/packagemanagercore.cpp1
-rw-r--r--src/libs/installer/packagemanagercore_p.cpp4
-rw-r--r--src/libs/installer/packagemanagergui.cpp244
-rw-r--r--src/libs/installer/packagemanagergui.h1
-rw-r--r--src/libs/installer/repository.cpp22
-rw-r--r--src/libs/installer/repository.h4
-rw-r--r--src/libs/installer/repositorycategory.cpp159
-rw-r--r--src/libs/installer/repositorycategory.h86
-rw-r--r--src/libs/installer/settings.cpp60
-rw-r--r--src/libs/installer/settings.h6
39 files changed, 932 insertions, 75 deletions
diff --git a/doc/images/ifw-repository-categories.png b/doc/images/ifw-repository-categories.png
new file mode 100644
index 000000000..afed7c1b9
--- /dev/null
+++ b/doc/images/ifw-repository-categories.png
Binary files differ
diff --git a/doc/includes/installerfw-examples-generating-online.qdocinc b/doc/includes/installerfw-examples-generating-online.qdocinc
new file mode 100644
index 000000000..f623b1d22
--- /dev/null
+++ b/doc/includes/installerfw-examples-generating-online.qdocinc
@@ -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.
+
diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc
index 22a15983b..bdd99c089 100644
--- a/doc/installerfw.qdoc
+++ b/doc/installerfw.qdoc
@@ -266,6 +266,10 @@
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
\e maintenancetool. The platform-specific executable file extension is
@@ -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
diff --git a/examples/doc/images/qtifw-examples-repository-categories.png b/examples/doc/images/qtifw-examples-repository-categories.png
new file mode 100644
index 000000000..afed7c1b9
--- /dev/null
+++ b/examples/doc/images/qtifw-examples-repository-categories.png
Binary files differ
diff --git a/examples/doc/repositorycategories.qdoc b/examples/doc/repositorycategories.qdoc
new file mode 100644
index 000000000..dca5ada43
--- /dev/null
+++ b/examples/doc/repositorycategories.qdoc
@@ -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
+*/
diff --git a/examples/examples.pro b/examples/examples.pro
index 377632b9c..bfe69a5c8 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -11,6 +11,7 @@ SUBDIRS += \
openreadme \
quitinstaller \
registerfileextension \
+ repositorycategories \
startmenu \
systeminfo \
stylesheet
diff --git a/examples/repositorycategories/README b/examples/repositorycategories/README
new file mode 100644
index 000000000..4a1f491c6
--- /dev/null
+++ b/examples/repositorycategories/README
@@ -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.
diff --git a/examples/repositorycategories/config/config.xml b/examples/repositorycategories/config/config.xml
new file mode 100644
index 000000000..c61e4893d
--- /dev/null
+++ b/examples/repositorycategories/config/config.xml
@@ -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>
diff --git a/examples/repositorycategories/packages/A/data/A.txt b/examples/repositorycategories/packages/A/data/A.txt
new file mode 100644
index 000000000..98114dd6e
--- /dev/null
+++ b/examples/repositorycategories/packages/A/data/A.txt
@@ -0,0 +1,2 @@
+Example content for package A.
+
diff --git a/examples/repositorycategories/packages/A/meta/package.xml b/examples/repositorycategories/packages/A/meta/package.xml
new file mode 100644
index 000000000..08cef59c0
--- /dev/null
+++ b/examples/repositorycategories/packages/A/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages/B/data/B.txt b/examples/repositorycategories/packages/B/data/B.txt
new file mode 100644
index 000000000..1ee864074
--- /dev/null
+++ b/examples/repositorycategories/packages/B/data/B.txt
@@ -0,0 +1,2 @@
+Example content for package B.
+
diff --git a/examples/repositorycategories/packages/B/meta/package.xml b/examples/repositorycategories/packages/B/meta/package.xml
new file mode 100644
index 000000000..44e90b9a2
--- /dev/null
+++ b/examples/repositorycategories/packages/B/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages2_forcategory1/A2Cagetory1/data/A2_category1.txt b/examples/repositorycategories/packages2_forcategory1/A2Cagetory1/data/A2_category1.txt
new file mode 100644
index 000000000..2b328a750
--- /dev/null
+++ b/examples/repositorycategories/packages2_forcategory1/A2Cagetory1/data/A2_category1.txt
@@ -0,0 +1,2 @@
+Example content for package A2, using category 1.
+
diff --git a/examples/repositorycategories/packages2_forcategory1/A2Cagetory1/meta/package.xml b/examples/repositorycategories/packages2_forcategory1/A2Cagetory1/meta/package.xml
new file mode 100644
index 000000000..02d175868
--- /dev/null
+++ b/examples/repositorycategories/packages2_forcategory1/A2Cagetory1/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages2_forcategory1/B2Category1/data/B2_category1.txt b/examples/repositorycategories/packages2_forcategory1/B2Category1/data/B2_category1.txt
new file mode 100644
index 000000000..56baa1709
--- /dev/null
+++ b/examples/repositorycategories/packages2_forcategory1/B2Category1/data/B2_category1.txt
@@ -0,0 +1,2 @@
+Example content for package B2, using category 1.
+
diff --git a/examples/repositorycategories/packages2_forcategory1/B2Category1/meta/package.xml b/examples/repositorycategories/packages2_forcategory1/B2Category1/meta/package.xml
new file mode 100644
index 000000000..0c0b0411d
--- /dev/null
+++ b/examples/repositorycategories/packages2_forcategory1/B2Category1/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages_forcategory1/ACagetory1/data/A_category1.txt b/examples/repositorycategories/packages_forcategory1/ACagetory1/data/A_category1.txt
new file mode 100644
index 000000000..e899cb202
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory1/ACagetory1/data/A_category1.txt
@@ -0,0 +1,2 @@
+Example content for package A, using category 1.
+
diff --git a/examples/repositorycategories/packages_forcategory1/ACagetory1/meta/package.xml b/examples/repositorycategories/packages_forcategory1/ACagetory1/meta/package.xml
new file mode 100644
index 000000000..1f0e795e9
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory1/ACagetory1/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages_forcategory1/BCategory1/data/B_category1.txt b/examples/repositorycategories/packages_forcategory1/BCategory1/data/B_category1.txt
new file mode 100644
index 000000000..d355cb633
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory1/BCategory1/data/B_category1.txt
@@ -0,0 +1,2 @@
+Example content for package B, using category 1.
+
diff --git a/examples/repositorycategories/packages_forcategory1/BCategory1/meta/package.xml b/examples/repositorycategories/packages_forcategory1/BCategory1/meta/package.xml
new file mode 100644
index 000000000..98b9776fd
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory1/BCategory1/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages_forcategory2/ACategory2/data/A_category2.txt b/examples/repositorycategories/packages_forcategory2/ACategory2/data/A_category2.txt
new file mode 100644
index 000000000..0a10aa452
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory2/ACategory2/data/A_category2.txt
@@ -0,0 +1,2 @@
+Example content for package A, using category 2.
+
diff --git a/examples/repositorycategories/packages_forcategory2/ACategory2/meta/package.xml b/examples/repositorycategories/packages_forcategory2/ACategory2/meta/package.xml
new file mode 100644
index 000000000..25a25f94e
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory2/ACategory2/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/packages_forcategory2/BCategory2/data/B_category2.txt b/examples/repositorycategories/packages_forcategory2/BCategory2/data/B_category2.txt
new file mode 100644
index 000000000..ebf02b452
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory2/BCategory2/data/B_category2.txt
@@ -0,0 +1,2 @@
+Example content for package B, using category 2.
+
diff --git a/examples/repositorycategories/packages_forcategory2/BCategory2/meta/package.xml b/examples/repositorycategories/packages_forcategory2/BCategory2/meta/package.xml
new file mode 100644
index 000000000..fa21c631b
--- /dev/null
+++ b/examples/repositorycategories/packages_forcategory2/BCategory2/meta/package.xml
@@ -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>
diff --git a/examples/repositorycategories/repositorycategories.pro b/examples/repositorycategories/repositorycategories.pro
new file mode 100644
index 000000000..37a0144b7
--- /dev/null
+++ b/examples/repositorycategories/repositorycategories.pro
@@ -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
diff --git a/src/libs/installer/constants.h b/src/libs/installer/constants.h
index 0e9646737..26c2a7dfe 100644
--- a/src/libs/installer/constants.h
+++ b/src/libs/installer/constants.h
@@ -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@";
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index f649a1ecb..bdca7e0f3 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -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
diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp
index ae29a2dcb..cb1579756 100644
--- a/src/libs/installer/metadatajob.cpp
+++ b/src/libs/installer/metadatajob.cpp
@@ -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
diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h
index e3f5aceea..973275de3 100644
--- a/src/libs/installer/metadatajob.h
+++ b/src/libs/installer/metadatajob.h
@@ -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
diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp
index 747960cee..44025de55 100644
--- a/src/libs/installer/packagemanagercore.cpp
+++ b/src/libs/installer/packagemanagercore.cpp
@@ -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.
diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp
index 195c16f30..1e271a0d5 100644
--- a/src/libs/installer/packagemanagercore_p.cpp
+++ b/src/libs/installer/packagemanagercore_p.cpp
@@ -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) {
diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp
index 287bf7d38..90ff11ea3 100644
--- a/src/libs/installer/packagemanagergui.cpp
+++ b/src/libs/installer/packagemanagergui.cpp
@@ -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,62 +1926,93 @@ 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);
+ 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;
+ }
- m_progressBar = new QProgressBar();
- m_progressBar->setRange(0, 0);
- m_progressBar->hide();
- m_vlayout->addWidget(m_progressBar);
- m_progressBar->setObjectName(QLatin1String("CompressedInstallProgressBar"));
+ 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_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);
- m_compressedButtonVisible = true;
+ 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()
@@ -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;
+ }
}
/*!
diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h
index 238e22a62..d67bc9bd9 100644
--- a/src/libs/installer/packagemanagergui.h
+++ b/src/libs/installer/packagemanagergui.h
@@ -314,6 +314,7 @@ public:
protected:
void entering();
+ void leaving();
void showEvent(QShowEvent *event);
private Q_SLOTS:
diff --git a/src/libs/installer/repository.cpp b/src/libs/installer/repository.cpp
index eb45573a9..249012786 100644
--- a/src/libs/installer/repository.cpp
+++ b/src/libs/installer/repository.cpp
@@ -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
{
@@ -199,6 +200,22 @@ void Repository::setDisplayName(const QString &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
*/
bool Repository::isCompressed() const
@@ -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();
}
}
diff --git a/src/libs/installer/repository.h b/src/libs/installer/repository.h
index b73e7bd4c..83393ead9 100644
--- a/src/libs/installer/repository.h
+++ b/src/libs/installer/repository.h
@@ -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;
};
diff --git a/src/libs/installer/repositorycategory.cpp b/src/libs/installer/repositorycategory.cpp
new file mode 100644
index 000000000..af7f6e818
--- /dev/null
+++ b/src/libs/installer/repositorycategory.cpp
@@ -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;
+}
+
+}
diff --git a/src/libs/installer/repositorycategory.h b/src/libs/installer/repositorycategory.h
new file mode 100644
index 000000000..315af761b
--- /dev/null
+++ b/src/libs/installer/repositorycategory.h
@@ -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
diff --git a/src/libs/installer/settings.cpp b/src/libs/installer/settings.cpp
index 5761387c6..21bbe8b4c 100644
--- a/src/libs/installer/settings.cpp
+++ b/src/libs/installer/settings.cpp
@@ -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);
+}
diff --git a/src/libs/installer/settings.h b/src/libs/installer/settings.h
index bc2db655b..0ee58639d 100644
--- a/src/libs/installer/settings.h
+++ b/src/libs/installer/settings.h
@@ -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;