Compare commits
	
		
			139 Commits
		
	
	
		
			v1.1.2
			...
			old-module
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 379f38bfe9 | |||
| 25acdeceba | |||
| 5462411d8a | |||
| b7f606cba1 | |||
| d51ba1e8df | |||
| 10dc285ed2 | |||
| 916f1c2fae | |||
| 0d9923ed0b | |||
| 8947b568a7 | |||
| 3dc5e111bb | |||
| c6e2cacd51 | |||
| 3d3e73f015 | |||
| fe012233e3 | |||
| e55d467f12 | |||
| c99c53714f | |||
| 9e10e21381 | |||
| c6884e1044 | |||
| fc2b4c54a5 | |||
| c04875fb6a | |||
| bac0c002d8 | |||
| 999e45c64a | |||
| a1012b9a82 | |||
| 39427ffc4c | |||
|   | 6bd75570de | ||
|   | acdcdea668 | ||
|   | edb405a4d1 | ||
|   | 3ad9c221d4 | ||
|   | dc3550eccd | ||
|   | fac16ac6a7 | ||
|   | a442ed8c12 | ||
|   | e08d687061 | ||
|   | 15f6369537 | ||
|   | f01b4ed2f8 | ||
|   | ea762b0945 | ||
|   | cae8acb4a4 | ||
|   | ae204d0108 | ||
|   | a2fae600c4 | ||
|   | 349acad491 | ||
|   | 9b4c4876bc | ||
|   | cc02baed9a | ||
|   | dfab089938 | ||
|   | 2dd9a7ba8a | ||
|   | 82f31f3cd9 | ||
|   | c5b01a574d | ||
|   | bc4789d57f | ||
|   | 0962b98494 | ||
|   | 108476c025 | ||
|   | 04a1bc9e2c | ||
|   | bff5e485f4 | ||
|   | 3c838436c2 | ||
|   | 644c9cf4f3 | ||
|   | dcaa378ddd | ||
|   | 14fd23dcef | ||
|   | f4bc7052e0 | ||
|   | 4f0f48d99d | ||
|   | 1344880f2e | ||
|   | 5704e146a0 | ||
|   | 85eb434b57 | ||
|   | 4b2ea61aa3 | ||
|   | 39d25eef1c | ||
|   | e82d32fe66 | ||
|   | a50ab49c22 | ||
|   | a66ab99ce8 | ||
|   | c3170a9dfb | ||
|   | 88437d3612 | ||
|   | 6db52a904e | ||
|   | 8f0403a3f8 | ||
|   | caf231b0d1 | ||
|   | 9ad86f81bb | ||
|   | 4d601f2e6a | ||
|   | 69f68d82e6 | ||
|   | 572a94e493 | ||
|   | 90454be1b9 | ||
|   | 6144404bd0 | ||
|   | 8f4d8d119c | ||
|   | e8c870205d | ||
|   | 081d7d47d1 | ||
|   | e92ecea3f5 | ||
|   | e158402478 | ||
|   | d242d077db | ||
|   | 5f9b46a820 | ||
|   | 9151d0fcee | ||
|   | 282f5bfade | ||
|   | eeb264f32e | ||
|   | 83606aaf9d | ||
|   | 322a7a212f | ||
|   | 40c7496f85 | ||
|   | f1fd52e790 | ||
|   | 12f6068622 | ||
|   | 16a3e2edb2 | ||
|   | 8fc9b907af | ||
|   | ecaf2c3076 | ||
|   | ea97927997 | ||
|   | 6795190216 | ||
|   | 51149e34d7 | ||
|   | a2674c652d | ||
|   | d4d6d17efe | ||
|   | b74d77a9c5 | ||
|   | 5074bebd32 | ||
|   | 76be9988fe | ||
|   | c85daf8f7b | ||
|   | 13e8b1e9b5 | ||
|   | e6ddf30512 | ||
|   | 9938d11ad1 | ||
|   | def4ee5c7e | ||
|   | 718f73c9c8 | ||
|   | 720415d8b7 | ||
|   | 0f0b9aa776 | ||
|   | 19d508ca2e | ||
|   | 7af1aeb132 | ||
|   | 3f7cabe832 | ||
|   | 341c9f4a7f | ||
|   | 03260efb3a | ||
|   | 18060db82d | ||
|   | b978f03618 | ||
|   | a4a6194a5d | ||
|   | 93cf06da82 | ||
|   | 901211c12d | ||
|   | e89a58a34b | ||
|   | fb36765982 | ||
|   | f1d2bfacce | ||
|   | 9703bd9058 | ||
|   | 0e2a178436 | ||
|   | fb70e29e29 | ||
|   | fbfa9d8038 | ||
|   | 59ab728502 | ||
|   | 476224d34e | ||
|   | 2aaf440fec | ||
|   | b416caa462 | ||
|   | 0c11b864e5 | ||
|   | 0f9ffb9e35 | ||
|   | 813d26ec4e | ||
|   | 5d44118b4b | ||
|   | 833e33f011 | ||
|   | ea3a03a8cc | ||
|   | 05bbeae2c3 | ||
|   | b9b923e69c | ||
|   | 809491c969 | ||
|   | febeb3281e | 
| @@ -8,6 +8,7 @@ AlignEscapedNewlines: DontAlign | ||||
| AllowAllParametersOfDeclarationOnNextLine: "false" | ||||
| AllowShortFunctionsOnASingleLine: Inline | ||||
| AllowShortIfStatementsOnASingleLine: "false" | ||||
| AllowShortLambdasOnASingleLine: All | ||||
| AllowShortLoopsOnASingleLine: "false" | ||||
| AlwaysBreakAfterReturnType: TopLevelDefinitions | ||||
| AlwaysBreakTemplateDeclarations: Yes | ||||
| @@ -28,7 +29,6 @@ ReflowComments: "false" | ||||
| SortIncludes: "true" | ||||
| SpaceAfterCStyleCast: "false" | ||||
| SpacesBeforeTrailingComments: "2" | ||||
| # SpaceInEmptyBlock: "true" | ||||
| SpacesInAngles: "true" | ||||
| SpacesInParentheses: "true" | ||||
| SpacesInSquareBrackets: "true" | ||||
|   | ||||
							
								
								
									
										35
									
								
								.clang-format.base
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,35 @@ | ||||
| #   SPDX-FileCopyrightText: no | ||||
| #   SPDX-License-Identifier: CC0-1.0 | ||||
| --- | ||||
| BasedOnStyle: WebKit | ||||
|  | ||||
| AlignAfterOpenBracket: Align | ||||
| AlignEscapedNewlines: DontAlign | ||||
| AllowAllParametersOfDeclarationOnNextLine: "false" | ||||
| AllowShortFunctionsOnASingleLine: Inline | ||||
| AllowShortIfStatementsOnASingleLine: "false" | ||||
| AllowShortLambdasOnASingleLine: All | ||||
| AllowShortLoopsOnASingleLine: "false" | ||||
| AlwaysBreakAfterReturnType: TopLevelDefinitions | ||||
| AlwaysBreakTemplateDeclarations: Yes | ||||
| BinPackArguments: "false" | ||||
| BinPackParameters: "false" | ||||
| BreakBeforeBraces: Allman | ||||
| BreakBeforeTernaryOperators: "true" | ||||
| BreakConstructorInitializers: BeforeComma | ||||
| ColumnLimit: 120 | ||||
| Cpp11BracedListStyle: "false" | ||||
| FixNamespaceComments: "true" | ||||
| IncludeBlocks: Preserve | ||||
| IndentWidth: "4" | ||||
| MaxEmptyLinesToKeep: "2" | ||||
| NamespaceIndentation: None | ||||
| PointerAlignment: Left | ||||
| ReflowComments: "false" | ||||
| SortIncludes: "true" | ||||
| SpaceAfterCStyleCast: "false" | ||||
| SpacesBeforeTrailingComments: "2" | ||||
| SpacesInAngles: "true" | ||||
| SpacesInParentheses: "true" | ||||
| SpacesInSquareBrackets: "true" | ||||
| Standard: Cpp11 | ||||
							
								
								
									
										74
									
								
								.github/workflows/nightly-neon.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -31,70 +31,30 @@ jobs: | ||||
|           path: ${{ env.BUILDDIR }} | ||||
|           repo: calamares/calamares | ||||
|       - name: "prepare env" | ||||
|         run: | | ||||
|           sudo apt-get update | ||||
|           sudo apt-get -y install git-core | ||||
|           sudo apt-get -y install \ | ||||
|             build-essential \ | ||||
|             cmake \ | ||||
|             extra-cmake-modules \ | ||||
|             gettext \ | ||||
|             kio-dev \ | ||||
|             libatasmart-dev \ | ||||
|             libboost-python-dev \ | ||||
|             libkf5config-dev \ | ||||
|             libkf5coreaddons-dev \ | ||||
|             libkf5i18n-dev \ | ||||
|             libkf5iconthemes-dev \ | ||||
|             libkf5parts-dev \ | ||||
|             libkf5service-dev \ | ||||
|             libkf5solid-dev \ | ||||
|             libkpmcore-dev \ | ||||
|             libparted-dev \ | ||||
|             libpolkit-qt5-1-dev \ | ||||
|             libqt5svg5-dev \ | ||||
|             libqt5webkit5-dev \ | ||||
|             libyaml-cpp-dev \ | ||||
|             os-prober \ | ||||
|             pkg-config \ | ||||
|             python3-dev \ | ||||
|             qtbase5-dev \ | ||||
|             qtdeclarative5-dev \ | ||||
|             qttools5-dev \ | ||||
|             qttools5-dev-tools | ||||
|         uses: calamares/actions/prepare-neon@v2 | ||||
|       - name: "prepare source" | ||||
|         uses: actions/checkout@v2 | ||||
|       - name: "prepare artifacts" | ||||
|         run: tar xvzf "$BUILDDIR/calamares.tar.gz" -C / --strip-components 1 | ||||
|       - name: "prepare build" | ||||
|         id: pre_build | ||||
|         run: | | ||||
|           test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; } | ||||
|           mkdir -p $BUILDDIR | ||||
|           test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; } | ||||
|           echo "::set-output name=message::"`git log -1 --abbrev-commit --pretty=oneline --no-decorate ${{ github.event.head_commit.id }}` | ||||
|       - name: "Calamares-Extensions: cmake" | ||||
|         working-directory: ${{ env.BUILDDIR }} | ||||
|         run: cmake $CMAKE_ARGS $SRCDIR | ||||
|       - name: "Calamares-Extensions: make" | ||||
|         working-directory: ${{ env.BUILDDIR }} | ||||
|         run: make -j2 VERBOSE=1 | ||||
|       - name: "Calamares-Extensions: install" | ||||
|         working-directory: ${{ env.BUILDDIR }} | ||||
|         run: make install VERBOSE=1 | ||||
|       - name: "build" | ||||
|         id: build | ||||
|         uses: calamares/actions/generic-build@v2 | ||||
|       - name: "notify: ok" | ||||
|         uses: rectalogic/notify-irc@v1 | ||||
|         if: ${{ success() && github.repository == 'calamares/calamares-extensions' }} | ||||
|         uses: calamares/actions/matrix-notify@v2 | ||||
|         with: | ||||
|           server: chat.freenode.net | ||||
|           nickname: cala-ci | ||||
|           channel: "#calamares" | ||||
|           message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" | ||||
|           token: ${{ secrets.MATRIX_TOKEN }} | ||||
|           room: ${{ secrets.MATRIX_ROOM }} | ||||
|           message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ steps.build.outputs.git_summary }}" | ||||
|       - name: "notify: fail" | ||||
|         uses: rectalogic/notify-irc@v1 | ||||
|         if: ${{ failure() && github.repository == 'calamares/calamares' }} | ||||
|         uses: calamares/actions/matrix-notify@v2 | ||||
|         with: | ||||
|           server: chat.freenode.net | ||||
|           nickname: cala-ci | ||||
|           channel: "#calamares" | ||||
|           message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ steps.pre_build.outputs.message }}" | ||||
|           token: ${{ secrets.MATRIX_TOKEN }} | ||||
|           room: ${{ secrets.MATRIX_ROOM }} | ||||
|           message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ steps.build.outputs.git_summary}}" | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										76
									
								
								.github/workflows/push.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -37,70 +37,30 @@ jobs: | ||||
|           path: ${{ env.BUILDDIR }} | ||||
|           repo: calamares/calamares | ||||
|       - name: "prepare env" | ||||
|         run: | | ||||
|           sudo apt-get update | ||||
|           sudo apt-get -y install git-core | ||||
|           sudo apt-get -y install \ | ||||
|             build-essential \ | ||||
|             cmake \ | ||||
|             extra-cmake-modules \ | ||||
|             gettext \ | ||||
|             kio-dev \ | ||||
|             libatasmart-dev \ | ||||
|             libboost-python-dev \ | ||||
|             libkf5config-dev \ | ||||
|             libkf5coreaddons-dev \ | ||||
|             libkf5i18n-dev \ | ||||
|             libkf5iconthemes-dev \ | ||||
|             libkf5parts-dev \ | ||||
|             libkf5service-dev \ | ||||
|             libkf5solid-dev \ | ||||
|             libkpmcore-dev \ | ||||
|             libparted-dev \ | ||||
|             libpolkit-qt5-1-dev \ | ||||
|             libqt5svg5-dev \ | ||||
|             libqt5webkit5-dev \ | ||||
|             libyaml-cpp-dev \ | ||||
|             os-prober \ | ||||
|             pkg-config \ | ||||
|             python3-dev \ | ||||
|             qtbase5-dev \ | ||||
|             qtdeclarative5-dev \ | ||||
|             qttools5-dev \ | ||||
|             qttools5-dev-tools | ||||
|         uses: calamares/actions/prepare-neon@v2 | ||||
|       - name: "prepare source" | ||||
|         uses: actions/checkout@v2 | ||||
|       - name: "prepare artifacts" | ||||
|         run: tar xvzf "$BUILDDIR/calamares.tar.gz" -C / --strip-components 1 | ||||
|       - name: "prepare build" | ||||
|         id: pre_build | ||||
|         run: | | ||||
|           test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; } | ||||
|           mkdir -p $BUILDDIR | ||||
|           test -f $SRCDIR/CMakeLists.txt || { echo "! Missing $SRCDIR/CMakeLists.txt" ; exit 1 ; } | ||||
|           echo "::set-output name=message::"`git log -1 --abbrev-commit --pretty=oneline --no-decorate ${{ github.event.head_commit.id }}` | ||||
|       - name: "Calamares-Extensions: cmake" | ||||
|         working-directory: ${{ env.BUILDDIR }} | ||||
|         run: cmake $CMAKE_ARGS $SRCDIR | ||||
|       - name: "Calamares-Extensions: make" | ||||
|         working-directory: ${{ env.BUILDDIR }} | ||||
|         run: make -j2 VERBOSE=1 | ||||
|       - name: "Calamares-Extensions: install" | ||||
|         working-directory: ${{ env.BUILDDIR }} | ||||
|         run: make install VERBOSE=1 | ||||
|       - name: "build" | ||||
|         id: build | ||||
|         uses: calamares/actions/generic-build@v2 | ||||
|       - name: "notify: ok" | ||||
|         uses: rectalogic/notify-irc@v1 | ||||
|         if: ${{ success() && github.repository == 'calamares/calamares-extensions' }} | ||||
|         uses: calamares/actions/matrix-notify@v2 | ||||
|         with: | ||||
|           server: chat.freenode.net | ||||
|           nickname: cala-ci | ||||
|           channel: "#calamares" | ||||
|           message: "OK ${{ github.workflow }} in ${{ github.repository }} ${{ github.actor }} on ${{ github.event.ref }}\n.. ${{ steps.pre_build.outputs.message }}" | ||||
|           token: ${{ secrets.MATRIX_TOKEN }} | ||||
|           room: ${{ secrets.MATRIX_ROOM }} | ||||
|           message: | | ||||
|             OK ${{ github.workflow }} in ${{ github.repository }} by ${{ github.actor }} on ${{ github.event.ref }} | ||||
|             .. ${{ steps.build.outputs.git-summary }} | ||||
|       - name: "notify: fail" | ||||
|         uses: rectalogic/notify-irc@v1 | ||||
|         if: ${{ failure() && github.repository == 'calamares/calamares' }} | ||||
|         if: ${{ failure() && github.repository == 'calamares/calamares-extensions' }} | ||||
|         uses: calamares/actions/matrix-notify@v2 | ||||
|         with: | ||||
|           server: chat.freenode.net | ||||
|           nickname: cala-ci | ||||
|           channel: "#calamares" | ||||
|           message: "FAIL ${{ github.workflow }} in ${{ github.repository }} ${{ github.actor }} on ${{ github.event.ref }}\n.. ${{ steps.pre_build.outputs.message }}\n.. DIFF ${{ github.event.compare }}" | ||||
|           token: ${{ secrets.MATRIX_TOKEN }} | ||||
|           room: ${{ secrets.MATRIX_ROOM }} | ||||
|           message: | | ||||
|             FAIL ${{ github.workflow }} in ${{ github.repository }} by ${{ github.actor }} on ${{ github.event.ref }} | ||||
|               .. ${{ steps.build.outputs.git-summary }} | ||||
|               .. ${{ github.event.compare }} | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -1,3 +1,6 @@ | ||||
| #   SPDX-FileCopyrightText: no | ||||
| #   SPDX-License-Identifier: CC0-1.0 | ||||
| # | ||||
| .kdev4/ | ||||
| build/ | ||||
| *.kdev4 | ||||
|   | ||||
							
								
								
									
										23
									
								
								.reuse/dep5
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | ||||
| Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | ||||
| Upstream-Name: Calamares-Extensions | ||||
| Source: https://github.com/calamares/calamares-extensions.git | ||||
|  | ||||
| ### BUILD ARTIFACTS / NOT SOURCE | ||||
| # | ||||
| # QRC Files are basically build artifacts | ||||
| # | ||||
| FILES: modules/*/*.qrc | ||||
| License: CC0-1.0 | ||||
| Copyright: no | ||||
|  | ||||
| # GitHub issue templates are not part of the source | ||||
| # | ||||
| Files: .github/ISSUE_TEMPLATE/* | ||||
| License: CC0-1.0 | ||||
| Copyright: no | ||||
|  | ||||
| # GitHub actions are not part of the source | ||||
| Files: .github/workflows/*.yml | ||||
| License: CC0-1.0 | ||||
| Copyright: no | ||||
|  | ||||
							
								
								
									
										43
									
								
								CHANGES
									
									
									
									
									
								
							
							
						
						| @@ -6,6 +6,49 @@ This is the changelog for Calamares-Extensions. For each release, the major | ||||
| changes and contributors are listed. Note that Calamares-Extensions does not | ||||
| have a historical changelog -- this log starts with version 1.0.0. | ||||
|  | ||||
| # 1.3.2 (2023-08-28) | ||||
|  | ||||
| We skipped a couple of releases in the release-notes, then tagged | ||||
| 1.3.1 without a version bump or release-notes. So 1.3.2 brings us | ||||
| back to "regular releases". | ||||
|  | ||||
| This release contains contributions from (alphabetically by first name): | ||||
|  - Anke Boersma | ||||
|  - Nathan Schulte | ||||
|  - Oliver Smith | ||||
|  - stravanpannala | ||||
|  - undef | ||||
|  | ||||
| Changes and new modules in this release: | ||||
|  - *mobile* Has new configuration options. (Thanks Nathan, Oliver) | ||||
|  - *unpackfsc* Uses a more portable invocation of tar. (Thanks sravanpannala) | ||||
|  | ||||
|  | ||||
| # 1.2.1 (2021-11-16) | ||||
|  | ||||
| The 1.2.0 release had no release-notes for that version, and failed to | ||||
| credit Anke and Otus. | ||||
|  | ||||
| Changes and new modules in this release: | ||||
|  - *unpackfsc* can use `fsarchiver` and unpack that, instead of squashfs; | ||||
|    a distro might choose one tool or the other. Currently, only *savedir* / | ||||
|    *restdir* mode (i.e. directories, not block-devices) are supported. | ||||
|  | ||||
| # 1.2.0 (2021-11-16) | ||||
|  | ||||
| This release contains contributions from (alphabetically by first name): | ||||
|  - Anke Boersma | ||||
|  - Otus9051 | ||||
|  | ||||
| Changes and new modules in this release: | ||||
|  - *refind* is a new module that installs the rEFInd bootloader. It can | ||||
|    be used instead of the *bootloader* module from core Calamares. (Thanks Anke) | ||||
|  - *unpackfsc* is a new module that uses `unsquashfs` directly. This may | ||||
|    be faster or more convenient than the *unpackfs* module from core Calamares. | ||||
|    The configuration file supports only one entry, but is otherwise easy to | ||||
|    adapt from an existing `unpackfs.conf`. | ||||
|  | ||||
|  | ||||
| # 1.1.2 (2021-05-14) | ||||
|  | ||||
| This release contains contributions from (alphabetically by first name): | ||||
|   | ||||
							
								
								
									
										125
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						| @@ -1,20 +1,15 @@ | ||||
| # === This file is part of Calamares - <https://github.com/calamares> === | ||||
| # | ||||
| #   Calamares is free software: you can redistribute it and/or modify | ||||
| #   it under the terms of the GNU General Public License as published by | ||||
| #   the Free Software Foundation, either version 3 of the License, or | ||||
| #   (at your option) any later version. | ||||
| #   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
| #   SPDX-License-Identifier: GPL-3.0-or-later | ||||
| # | ||||
| #   Calamares is distributed in the hope that it will be useful, | ||||
| #   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| #   GNU General Public License for more details. | ||||
| ### | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License | ||||
| #   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
| #   Calamares-Examples is Free Software: see the License-Identifier above. | ||||
| # | ||||
| #   SPDX-License-Identifier: GPL-3.0+ | ||||
| #   License-Filename: LICENSE | ||||
| #   Individual files may have different licenses (like the CMake | ||||
| #   infrastructure, which is BSD-2-Clause licensed). Check the SPDX | ||||
| #   identifiers in each file. | ||||
| # | ||||
| ### | ||||
| # | ||||
| @@ -36,22 +31,67 @@ | ||||
| #   USE_*           : fills in SKIP_MODULES for modules called *-<something> | ||||
| # In this repository, there is just one "group" to which USE_* applies: | ||||
| #   USE_os          : operating-system-specific modules. | ||||
| cmake_minimum_required(VERSION 3.3 FATAL_ERROR) | ||||
| # | ||||
| ### NOTES | ||||
| # | ||||
| # Call this CMake file in script mode, e.g. `cmake -P CMakeLists.txt` | ||||
| # to print out version information. Use `cmake -DVERSION_STYLE=short` | ||||
| # to get just the short versioning. | ||||
| # | ||||
| cmake_minimum_required(VERSION 3.16 FATAL_ERROR) | ||||
|  | ||||
| set( CALAMARES_EXTENSIONS_VERSION 1.4.0 ) | ||||
|  | ||||
| include( ${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake ) | ||||
| if ( CMAKE_SCRIPT_MODE_FILE ) | ||||
|     report_version( ${CALAMARES_EXTENSIONS_VERSION} ${CMAKE_CURRENT_LIST_DIR} ) | ||||
|     return() | ||||
| endif() | ||||
|  | ||||
| project(calamares-extensions | ||||
|     VERSION 1.1.2 | ||||
|     VERSION ${CALAMARES_EXTENSIONS_VERSION} | ||||
|     LANGUAGES CXX | ||||
| ) | ||||
|  | ||||
| set( CMAKE_CXX_STANDARD 17 ) | ||||
| set( CMAKE_CXX_STANDARD_REQUIRED ON ) | ||||
|  | ||||
|  | ||||
| if(WITH_QT6) | ||||
|     set(kfname "KF6") | ||||
|     set(KF_VERSION 5.240) # KDE Neon weirdness | ||||
| else() | ||||
|     message(STATUS "Building Calamares with Qt5") | ||||
|     set(kfname "KF5") | ||||
|     set(KF_VERSION 5.78) | ||||
|     # API that was deprecated before Qt 5.15 causes a compile error | ||||
|     add_compile_definitions(QT_DISABLE_DEPRECATED_BEFORE=0x050f00) | ||||
| endif() | ||||
|  | ||||
| include( FeatureSummary ) | ||||
| find_package(${kfname}CoreAddons ${KF_VERSION} QUIET) | ||||
| set_package_properties( | ||||
|     ${kfname}CoreAddons | ||||
|     PROPERTIES | ||||
|     TYPE REQUIRED | ||||
|     DESCRIPTION "KDE Framework CoreAddons" | ||||
|     URL "https://api.kde.org/frameworks/" | ||||
|     PURPOSE "Essential Framework for AboutData and Macros" | ||||
| ) | ||||
|  | ||||
| # On developer's machine, the user package registry breaks | ||||
| # consumers by loading the developer's config from a build | ||||
| # directory (which doesn't have the rest of the config | ||||
| # installed inside it). | ||||
| find_package(Calamares 3.2.26 REQUIRED NO_CMAKE_PACKAGE_REGISTRY) | ||||
| set( CALAMARES_VERSION_REQUIRED 3.3.0 ) | ||||
| find_package(Calamares ${CALAMARES_VERSION_REQUIRED} NO_CMAKE_PACKAGE_REGISTRY) | ||||
| if (NOT TARGET Calamares::calamares OR NOT TARGET Calamares::calamaresui) | ||||
|     message(FATAL_ERROR "Calamares found with missing CMake targets") | ||||
|     find_package(Calamares ${CALAMARES_VERSION_REQUIRED} REQUIRED) | ||||
| endif() | ||||
|  | ||||
| find_package(YAMLCPP REQUIRED)  # Needed to untangle some dependencies before Calamares 3.2.36 | ||||
| message(STATUS "Found Calamares version ${Calamares_VERSION}") | ||||
| message(STATUS "              libraries ${Calamares_LIB_DIRS}") | ||||
| message(STATUS "") | ||||
|  | ||||
| ### CMAKE SETUP | ||||
| # | ||||
| @@ -71,6 +111,7 @@ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.10.0") | ||||
|         "K_EXPORT_PLASMA_RUNNER" | ||||
|     ) | ||||
| endif() | ||||
| include( CTest ) | ||||
|  | ||||
|  | ||||
| ### BRANDING | ||||
| @@ -78,18 +119,19 @@ endif() | ||||
| # Typically you would use only one branding, since that's | ||||
| # the (single) branding for your distro. | ||||
| # | ||||
| calamares_add_branding_subdirectory( branding/default NAME default ) | ||||
| calamares_add_branding_subdirectory( branding/default-mobile NAME default-mobile ) | ||||
| calamares_add_branding_subdirectory( branding/fancy NAME fancy ) | ||||
| # calamares_add_branding_subdirectory( branding/default NAME default ) | ||||
| # calamares_add_branding_subdirectory( branding/default-mobile NAME default-mobile ) | ||||
| # calamares_add_branding_subdirectory( branding/fancy NAME fancy ) | ||||
|  | ||||
| # This one has files in subdirectories | ||||
| calamares_add_branding_subdirectory( branding/samegame NAME samegame SUBDIRECTORIES img ) | ||||
| # calamares_add_branding_subdirectory( branding/samegame NAME samegame SUBDIRECTORIES img ) | ||||
|  | ||||
| # KaOS branding, with translations, note we can *NAME* something | ||||
| # different from the source directory it lives in; this will be installed | ||||
| # to a directory called *NAME* though -- and the `branding.desc` must | ||||
| # have a *componentName* that matches this *NAME*. | ||||
| calamares_add_branding_subdirectory( branding/kaos_branding NAME kaos ) | ||||
| # calamares_add_branding_subdirectory( branding/kaos_branding NAME kaos ) | ||||
| calamares_add_branding_subdirectory( branding/artix NAME artix ) | ||||
|  | ||||
| ### MODULES | ||||
| # | ||||
| @@ -97,28 +139,25 @@ calamares_add_branding_subdirectory( branding/kaos_branding NAME kaos ) | ||||
| # | ||||
| set(LIST_SKIPPED_MODULES "") | ||||
|  | ||||
| calamares_add_module_subdirectory( modules/filekeeper LIST_SKIPPED_MODULES )  # C++ job | ||||
| calamares_add_module_subdirectory( modules/freebsddisk LIST_SKIPPED_MODULES )  # C++ viewmodule | ||||
| calamares_add_module_subdirectory( modules/mobile LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/os-freebsd LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/os-nixos LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/slowpython LIST_SKIPPED_MODULES )  # Python job | ||||
| # calamares_add_module_subdirectory( modules/freebsddisk LIST_SKIPPED_MODULES )  # C++ viewmodule | ||||
| # calamares_add_module_subdirectory( modules/mobile LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/os-freebsd LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/os-nixos LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/refind LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/slowpython LIST_SKIPPED_MODULES )  # Python job | ||||
| # calamares_add_module_subdirectory( modules/unpackfsc LIST_SKIPPED_MODULES ) | ||||
|  | ||||
| calamares_add_module_subdirectory( modules/basestrap LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/services-artix LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/services-runit LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/services-dinit LIST_SKIPPED_MODULES ) | ||||
| # calamares_add_module_subdirectory( modules/services-s6 LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/postcfg LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/packagechooser LIST_SKIPPED_MODULES ) | ||||
| calamares_add_module_subdirectory( modules/packagechooserq LIST_SKIPPED_MODULES ) | ||||
|  | ||||
| message(STATUS "Calamares extensions ${CALAMARES_EXTENSIONS_VERSION} for Calamares version ${Calamares_VERSION}") | ||||
|  | ||||
| # If modules cannot be built, they usually call a macro | ||||
| # which builds a list of explanations; show that list. | ||||
| calamares_explain_skipped_modules( ${LIST_SKIPPED_MODULES} ) | ||||
|  | ||||
| ### RELEASE SUPPORT | ||||
| # | ||||
| # | ||||
| set( CALAMARES_VERSION ${calamares-extensions_VERSION_MAJOR}.${calamares-extensions_VERSION_MINOR}.${calamares-extensions_VERSION_PATCH} ) | ||||
| # In rare cases we have hotfix-releases with a tweak | ||||
| if( calamares-extensions_VERSION_TWEAK ) | ||||
|     set( CALAMARES_VERSION "${calamares-extensions_VERSION}.${calamares-extensions_VERSION_TWEAK}" ) | ||||
| endif() | ||||
| set( CALAMARES_VERSION_SHORT "${CALAMARES_VERSION}" ) | ||||
|  | ||||
| add_custom_target(show-version | ||||
|     ${CMAKE_COMMAND} -E echo CALAMARES_VERSION=${CALAMARES_VERSION_SHORT} | ||||
|     USES_TERMINAL | ||||
| ) | ||||
|   | ||||
							
								
								
									
										90
									
								
								CMakeModules/ExtendedVersion.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,90 @@ | ||||
| # === This file is part of Calamares - <https://calamares.io> === | ||||
| # | ||||
| #   SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org> | ||||
| #   SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org> | ||||
| #   SPDX-License-Identifier: BSD-2-Clause | ||||
| # | ||||
| ### | ||||
| # | ||||
| # This file defines one function for extending a VERSION-like value | ||||
| # with date and git information (if desired). | ||||
| # | ||||
| # - extend_version( version-string short_var long_var ) | ||||
| #   Calling this function will copy *version-string* (which would typically | ||||
| #   be a semver-style string, like "3.2.40") into the variable *short_var*. | ||||
| #   The *version-string* plus date and git information (if git is available), | ||||
| #   is copied into the varialbe *long_var*, in the format {version}-{date}-{hash} | ||||
| # | ||||
| # A helper function that may be used independently: | ||||
| # | ||||
| # - get_git_version_info( out_var ) | ||||
| #   If relevant and possible (e.g. it is a git checkout and git is availablle | ||||
| #   in the environment), put git versioning information in *out_var*. | ||||
| # | ||||
| # A convenience function for use from script-mode for version reporting: | ||||
| # | ||||
| # - report_version( version top_dir ) | ||||
| #   Call this with an intended version string (e.g. "1.1") and | ||||
| #   the top-level source directory (e.g. `${CMAKE_CURRENT_LIST_DIR}` | ||||
| #   or `${CMAKE_SOURCE_DIR}` .. in script mode, the latter is not defined). | ||||
| # | ||||
|  | ||||
| function( get_git_version_info out_var ) | ||||
|     set(CMAKE_VERSION_SOURCE "") | ||||
|     if(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD) | ||||
|         find_program(GIT_EXECUTABLE NAMES git git.cmd) | ||||
|         mark_as_advanced(GIT_EXECUTABLE) | ||||
|         if(GIT_EXECUTABLE) | ||||
|             execute_process( | ||||
|                 COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=8 HEAD | ||||
|                 OUTPUT_VARIABLE head | ||||
|                 OUTPUT_STRIP_TRAILING_WHITESPACE | ||||
|                 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | ||||
|             ) | ||||
|             if(head) | ||||
|                 set(CMAKE_VERSION_SOURCE "${head}") | ||||
|                 execute_process( | ||||
|                     COMMAND ${GIT_EXECUTABLE} update-index -q --refresh | ||||
|                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | ||||
|                 ) | ||||
|                 execute_process( | ||||
|                     COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD -- | ||||
|                     OUTPUT_VARIABLE dirty | ||||
|                     OUTPUT_STRIP_TRAILING_WHITESPACE | ||||
|                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | ||||
|                 ) | ||||
|                 if(dirty) | ||||
|                     set(CMAKE_VERSION_SOURCE "${CMAKE_VERSION_SOURCE}-dirty") | ||||
|                 endif() | ||||
|             endif() | ||||
|         endif() | ||||
|     endif() | ||||
|     set( ${out_var} "${CMAKE_VERSION_SOURCE}" PARENT_SCOPE ) | ||||
| endfunction() | ||||
|  | ||||
| function( extend_version version short_var long_var ) | ||||
|     set( ${short_var} "${version}" PARENT_SCOPE ) | ||||
|  | ||||
|     # Additional info for non-release builds which want "long" version info | ||||
|     # with date and git information (commit, dirty status). | ||||
|     set( _v "${version}" ) | ||||
|     string( TIMESTAMP CALAMARES_VERSION_DATE "%Y%m%d" ) | ||||
|     if( CALAMARES_VERSION_DATE GREATER 0 ) | ||||
|         set( _v ${_v}.${CALAMARES_VERSION_DATE} ) | ||||
|     endif() | ||||
|     get_git_version_info( _gitv ) | ||||
|     if( _gitv ) | ||||
|         set( _v "${_v}-${_gitv}" ) | ||||
|     endif() | ||||
|     set( ${long_var} "${_v}" PARENT_SCOPE ) | ||||
| endfunction() | ||||
|  | ||||
| function( report_version version top_dir ) | ||||
|     set( CMAKE_SOURCE_DIR ${top_dir} ) | ||||
|     extend_version( ${version} _vshort _vlong ) | ||||
|     if ( "x${VERSION_STYLE}" STREQUAL "xshort" ) | ||||
|         message( "${_vshort}" ) | ||||
|     else() | ||||
|         message( "${_vlong}" ) | ||||
|     endif() | ||||
| endfunction() | ||||
							
								
								
									
										40
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,40 @@ | ||||
| <!-- SPDX-FileCopyrightText: no | ||||
|      SPDX-License-Identifier: CC0-1.0 | ||||
| --> | ||||
|  | ||||
| # Contributing to Calamares Extensions | ||||
|  | ||||
| Welcome to Calamares! We're happy that you would like to add | ||||
| something to Calamares -- by extending it! | ||||
| This contribution guide is minimal: | ||||
| all the **technical** parts of contributing to | ||||
| Calamares Extensions are the same as [contributing to Calamares](https://github.com/calamares/calamares/CONTRIBUTING.md). | ||||
|  | ||||
|  | ||||
| ## Code of Conduct | ||||
|  | ||||
| The Calamares community -- of developers, translators, and downstream (distro) users -- | ||||
| aims to be courteous, professional, and inclusive. Harrassment, discriminatory | ||||
| statements and abuse are not tolerated. In general, we apply the | ||||
| [KDE Code of Conduct](https://www.kde.org/code-of-conduct/) and the | ||||
| [GNOME Code of Conduct](https://wiki.gnome.org/Foundation/CodeOfConduct) (the | ||||
| rules of decent behavior in both communities are pretty much the same). | ||||
|  | ||||
| > See the [CoC section on the wiki](https://github.com/calamares/calamares/wiki#code-of-conduct) | ||||
| > for a longer text. To report a problem, please contact the maintainer, | ||||
| > Adriaan de Groot, or the KDE Community Working Group. | ||||
|  | ||||
|  | ||||
| ## Join the Conversation | ||||
|  | ||||
| GitHub Issues are **one** place for discussing Calamares and its extensions if there are concrete | ||||
| problems or a new feature to discuss. | ||||
| Issues are not a help channel. | ||||
| Visit Matrix for help with configuration or compilation. | ||||
|  | ||||
| Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/) | ||||
| room, `#calamares:kde.org`. Responsiveness is best during the day | ||||
| in Europe, but feel free to idle. | ||||
| Matrix is persistent, and we'll see your message eventually. | ||||
|  | ||||
| * [](https://webchat.kde.org/#/room/%23calamares:kde.org) | ||||
							
								
								
									
										24
									
								
								LICENSES/BSD-2-Clause.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,24 @@ | ||||
|  | ||||
|  Copyright 2019 Adriaan de Groot <groot@kde.org> | ||||
|  | ||||
|  Redistribution and use in source and binary forms, with or without | ||||
|  modification, are permitted provided that the following conditions | ||||
|  are met: | ||||
|  | ||||
|    1. Redistributions of source code must retain the above copyright | ||||
|       notice, this list of conditions and the following disclaimer. | ||||
|    2. Redistributions in binary form must reproduce the above copyright | ||||
|       notice, this list of conditions and the following disclaimer in the | ||||
|       documentation and/or other materials provided with the distribution. | ||||
|  | ||||
|  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
|  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
|  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
|  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||
|  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||
|  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||
|  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||
|  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||
|  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||
|  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||
|  POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										119
									
								
								LICENSES/CC0-1.0.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,119 @@ | ||||
| Creative Commons Legal Code | ||||
|  | ||||
| CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES | ||||
| NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE | ||||
| AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION | ||||
| ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE | ||||
| OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS | ||||
| LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION | ||||
| OR WORKS PROVIDED HEREUNDER. | ||||
|  | ||||
| Statement of Purpose | ||||
|  | ||||
| The laws of most jurisdictions throughout the world automatically confer exclusive | ||||
| Copyright and Related Rights (defined below) upon the creator and subsequent | ||||
| owner(s) (each and all, an "owner") of an original work of authorship and/or | ||||
| a database (each, a "Work"). | ||||
|  | ||||
| Certain owners wish to permanently relinquish those rights to a Work for the | ||||
| purpose of contributing to a commons of creative, cultural and scientific | ||||
| works ("Commons") that the public can reliably and without fear of later claims | ||||
| of infringement build upon, modify, incorporate in other works, reuse and | ||||
| redistribute as freely as possible in any form whatsoever and for any purposes, | ||||
| including without limitation commercial purposes. These owners may contribute | ||||
| to the Commons to promote the ideal of a free culture and the further production | ||||
| of creative, cultural and scientific works, or to gain reputation or greater | ||||
| distribution for their Work in part through the use and efforts of others. | ||||
|  | ||||
| For these and/or other purposes and motivations, and without any expectation | ||||
| of additional consideration or compensation, the person associating CC0 with | ||||
| a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright | ||||
| and Related Rights in the Work, voluntarily elects to apply CC0 to the Work | ||||
| and publicly distribute the Work under its terms, with knowledge of his or | ||||
| her Copyright and Related Rights in the Work and the meaning and intended | ||||
| legal effect of CC0 on those rights. | ||||
|  | ||||
| 1. Copyright and Related Rights. A Work made available under CC0 may be protected | ||||
| by copyright and related or neighboring rights ("Copyright and Related Rights"). | ||||
| Copyright and Related Rights include, but are not limited to, the following: | ||||
|  | ||||
| i. the right to reproduce, adapt, distribute, perform, display, communicate, | ||||
| and translate a Work; | ||||
|  | ||||
|       ii. moral rights retained by the original author(s) and/or performer(s); | ||||
|  | ||||
| iii. publicity and privacy rights pertaining to a person's image or likeness | ||||
| depicted in a Work; | ||||
|  | ||||
| iv. rights protecting against unfair competition in regards to a Work, subject | ||||
| to the limitations in paragraph 4(a), below; | ||||
|  | ||||
| v. rights protecting the extraction, dissemination, use and reuse of data | ||||
| in a Work; | ||||
|  | ||||
| vi. database rights (such as those arising under Directive 96/9/EC of the | ||||
| European Parliament and of the Council of 11 March 1996 on the legal protection | ||||
| of databases, and under any national implementation thereof, including any | ||||
| amended or successor version of such directive); and | ||||
|  | ||||
| vii. other similar, equivalent or corresponding rights throughout the world | ||||
| based on applicable law or treaty, and any national implementations thereof. | ||||
|  | ||||
| 2. Waiver. To the greatest extent permitted by, but not in contravention of, | ||||
| applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and | ||||
| unconditionally waives, abandons, and surrenders all of Affirmer's Copyright | ||||
| and Related Rights and associated claims and causes of action, whether now | ||||
| known or unknown (including existing as well as future claims and causes of | ||||
| action), in the Work (i) in all territories worldwide, (ii) for the maximum | ||||
| duration provided by applicable law or treaty (including future time extensions), | ||||
| (iii) in any current or future medium and for any number of copies, and (iv) | ||||
| for any purpose whatsoever, including without limitation commercial, advertising | ||||
| or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the | ||||
| benefit of each member of the public at large and to the detriment of Affirmer's | ||||
| heirs and successors, fully intending that such Waiver shall not be subject | ||||
| to revocation, rescission, cancellation, termination, or any other legal or | ||||
| equitable action to disrupt the quiet enjoyment of the Work by the public | ||||
| as contemplated by Affirmer's express Statement of Purpose. | ||||
|  | ||||
| 3. Public License Fallback. Should any part of the Waiver for any reason be | ||||
| judged legally invalid or ineffective under applicable law, then the Waiver | ||||
| shall be preserved to the maximum extent permitted taking into account Affirmer's | ||||
| express Statement of Purpose. In addition, to the extent the Waiver is so | ||||
| judged Affirmer hereby grants to each affected person a royalty-free, non | ||||
| transferable, non sublicensable, non exclusive, irrevocable and unconditional | ||||
| license to exercise Affirmer's Copyright and Related Rights in the Work (i) | ||||
| in all territories worldwide, (ii) for the maximum duration provided by applicable | ||||
| law or treaty (including future time extensions), (iii) in any current or | ||||
| future medium and for any number of copies, and (iv) for any purpose whatsoever, | ||||
| including without limitation commercial, advertising or promotional purposes | ||||
| (the "License"). The License shall be deemed effective as of the date CC0 | ||||
| was applied by Affirmer to the Work. Should any part of the License for any | ||||
| reason be judged legally invalid or ineffective under applicable law, such | ||||
| partial invalidity or ineffectiveness shall not invalidate the remainder of | ||||
| the License, and in such case Affirmer hereby affirms that he or she will | ||||
| not (i) exercise any of his or her remaining Copyright and Related Rights | ||||
| in the Work or (ii) assert any associated claims and causes of action with | ||||
| respect to the Work, in either case contrary to Affirmer's express Statement | ||||
| of Purpose. | ||||
|  | ||||
|    4. Limitations and Disclaimers. | ||||
|  | ||||
| a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, | ||||
| licensed or otherwise affected by this document. | ||||
|  | ||||
| b. Affirmer offers the Work as-is and makes no representations or warranties | ||||
| of any kind concerning the Work, express, implied, statutory or otherwise, | ||||
| including without limitation warranties of title, merchantability, fitness | ||||
| for a particular purpose, non infringement, or the absence of latent or other | ||||
| defects, accuracy, or the present or absence of errors, whether or not discoverable, | ||||
| all to the greatest extent permissible under applicable law. | ||||
|  | ||||
| c. Affirmer disclaims responsibility for clearing rights of other persons | ||||
| that may apply to the Work or any use thereof, including without limitation | ||||
| any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims | ||||
| responsibility for obtaining any necessary consents, permissions or other | ||||
| rights required for any use of the Work. | ||||
|  | ||||
| d. Affirmer understands and acknowledges that Creative Commons is not a party | ||||
| to this document and has no duty or obligation with respect to this CC0 or | ||||
| use of the Work. | ||||
							
								
								
									
										24
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,3 +1,7 @@ | ||||
| <!-- SPDX-FileCopyrightText: no | ||||
|      SPDX-License-Identifier: CC0-1.0 | ||||
| --> | ||||
|  | ||||
| # Calamares Branding and Module Examples | ||||
|  | ||||
| > A *branding component* in Calamares is a description of the | ||||
| @@ -36,7 +40,10 @@ and documentation for the framework that Calamares ships with. | ||||
|    (probably moreso than the default slideshow). | ||||
|  - [`kaos_branding/`](branding/kaos_branding/branding.desc) | ||||
|    is a copy of the KaOS branding component, which | ||||
|    has translations and a bunch of fancy graphics. | ||||
|    has translations and a bunch of fancy graphics for the | ||||
|    slideshow. Plus it includes examples of using different | ||||
|    QML options for a vertical navigation bar and horizontal | ||||
|    sidebar. | ||||
|  - [`samegame/` ](branding/default/branding.desc) | ||||
|    is a copy of the Qt Company "Same Game" QML demo. It | ||||
|    shows that **any** QML can be used for branding purposes. | ||||
| @@ -207,3 +214,18 @@ usually called `main.py` which defines a `run()` function. | ||||
|  | ||||
| The API is loosely documented in the | ||||
| [developer guide](https://github.com/calamares/calamares/wiki/Develop-Guide). | ||||
|  | ||||
| # Join the Conversation | ||||
|  | ||||
| GitHub Issues are **one** place for discussing Calamares (and Calamares Extensions) | ||||
| if there are concrete | ||||
| problems or a new feature to discuss. | ||||
| Issues are not a help channel. | ||||
| Visit Matrix for help with configuration or compilation. | ||||
|  | ||||
| Regular Calamares development chit-chat happens in a [Matrix](https://matrix.org/) | ||||
| room, `#calamares:kde.org`. Responsiveness is best during the day | ||||
| in Europe, but feel free to idle. | ||||
| Matrix is persistent, and we'll see your message eventually. | ||||
|  | ||||
| * [](https://webchat.kde.org/#/room/%23calamares:kde.org) | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								branding/artix/artix.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 70 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/banner.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 6.8 KiB | 
							
								
								
									
										239
									
								
								branding/artix/branding.desc
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,239 @@ | ||||
| # SPDX-FileCopyrightText: no | ||||
| # SPDX-License-Identifier: CC0-1.0 | ||||
| # | ||||
| # Product branding information. This influences some global | ||||
| # user-visible aspects of Calamares, such as the product | ||||
| # name, window behavior, and the slideshow during installation. | ||||
| # | ||||
| # Additional styling can be done using the stylesheet.qss | ||||
| # file, also in the branding directory. | ||||
| --- | ||||
| componentName:  artix | ||||
|  | ||||
|  | ||||
| ### WELCOME / OVERALL WORDING | ||||
| # | ||||
| # These settings affect some overall phrasing and looks, | ||||
| # which are most visible in the welcome page. | ||||
|  | ||||
| # This selects between different welcome texts. When false, uses | ||||
| # the traditional "Welcome to the %1 installer.", and when true, | ||||
| # uses "Welcome to the Calamares installer for %1." This allows | ||||
| # to distinguish this installer from other installers for the | ||||
| # same distribution. | ||||
| welcomeStyleCalamares:   false | ||||
|  | ||||
| # Should the welcome image (productWelcome, below) be scaled | ||||
| # up beyond its natural size? If false, the image does not grow | ||||
| # with the window but remains the same size throughout (this | ||||
| # may have surprising effects on HiDPI monitors). | ||||
| welcomeExpandingLogo:   true | ||||
|  | ||||
| ### WINDOW CONFIGURATION | ||||
| # | ||||
| # The settings here affect the placement of the Calamares | ||||
| # window through hints to the window manager and initial | ||||
| # sizing of the Calamares window. | ||||
|  | ||||
| # Size and expansion policy for Calamares. | ||||
| #  - "normal" or unset, expand as needed, use *windowSize* | ||||
| #  - "fullscreen", start as large as possible, ignore *windowSize* | ||||
| #  - "noexpand", don't expand automatically, use *windowSize* | ||||
| windowExpanding:    normal | ||||
|  | ||||
| # Size of Calamares window, expressed as w,h. Both w and h | ||||
| # may be either pixels (suffix px) or font-units (suffix em). | ||||
| #   e.g.    "800px,600px" | ||||
| #           "60em,480px" | ||||
| # This setting is ignored if "fullscreen" is selected for | ||||
| # *windowExpanding*, above. If not set, use constants defined | ||||
| # in CalamaresUtilsGui, 800x520. | ||||
| windowSize: 800px,520px | ||||
|  | ||||
| # Placement of Calamares window. Either "center" or "free". | ||||
| # Whether "center" actually works does depend on the window | ||||
| # manager in use (and only makes sense if you're not using | ||||
| # *windowExpanding* set to "fullscreen"). | ||||
| windowPlacement: center | ||||
|  | ||||
| ### PANELS CONFIGURATION | ||||
| # | ||||
| # Calamares has a main content area, and two panels (navigation | ||||
| # and progress / sidebar). The panels can be controlled individually, | ||||
| # or switched off. If both panels are switched off, the layout of | ||||
| # the main content area loses its margins, on the assumption that | ||||
| # you're doing something special. | ||||
|  | ||||
| # Kind of sidebar (panel on the left, showing progress). | ||||
| #  - "widget" or unset, use traditional sidebar (logo, items) | ||||
| #  - "none", hide it entirely | ||||
| #  - "qml", use calamares-sidebar.qml from branding folder | ||||
| # In addition, you **may** specify a side, separated by a comma, | ||||
| # from the kind. Valid sides are: | ||||
| #  - "left" (if not specified, uses this) | ||||
| #  - "right" | ||||
| #  - "top" | ||||
| #  - "bottom" | ||||
| # For instance, "widget,right" is valid; so is "qml", which defaults | ||||
| # to putting the sidebar on the left. Also valid is "qml,top". | ||||
| # While "widget,top" is valid, the widgets code is **not** flexible | ||||
| # and results will be terrible. | ||||
| sidebar: widget | ||||
|  | ||||
| # Kind of navigation (button panel on the bottom). | ||||
| #  - "widget" or unset, use traditional navigation | ||||
| #  - "none", hide it entirely | ||||
| #  - "qml", use calamares-navigation.qml from branding folder | ||||
| # In addition, you **may** specify a side, separated by a comma, | ||||
| # from the kind. The same sides are valid as for *sidebar*, | ||||
| # except the default is *bottom*. | ||||
| navigation: widget | ||||
|  | ||||
|  | ||||
| ### STRINGS, IMAGES AND COLORS | ||||
| # | ||||
| # This section contains the "branding proper" of names | ||||
| # and images, rather than global-look settings. | ||||
|  | ||||
| # These are strings shown to the user in the user interface. | ||||
| # There is no provision for translating them -- since they | ||||
| # are names, the string is included as-is. | ||||
| # | ||||
| # The four Url strings are the Urls used by the buttons in | ||||
| # the welcome screen, and are not shown to the user. Clicking | ||||
| # on the "Support" button, for instance, opens the link supportUrl. | ||||
| # If a Url is empty, the corresponding button is not shown. | ||||
| # | ||||
| # bootloaderEntryName is how this installation / distro is named | ||||
| # in the boot loader (e.g. in the GRUB menu). | ||||
| # | ||||
| # These strings support substitution from /etc/os-release | ||||
| # if KDE Frameworks 5.58 are available at build-time. When | ||||
| # enabled, ${varname} is replaced by the equivalent value | ||||
| # from os-release. All the supported var-names are in all-caps, | ||||
| # and are listed on the FreeDesktop.org site, | ||||
| #       https://www.freedesktop.org/software/systemd/man/os-release.html | ||||
| # Note that ANSI_COLOR and CPE_NAME don't make sense here, and | ||||
| # are not supported (the rest are). Remember to quote the string | ||||
| # if it contains substitutions, or you'll get YAML exceptions. | ||||
| # | ||||
| # The *Url* entries are used on the welcome page, and they | ||||
| # are visible as buttons there if the corresponding *show* keys | ||||
| # are set to "true" (they can also be overridden). | ||||
| strings: | ||||
|     productName:         Artix Linux | ||||
|     shortProductName:    Artix | ||||
|     version:             rolling | ||||
|     shortVersion:        rolling | ||||
|     versionedName:       Artix Linux "rolling" | ||||
|     shortVersionedName:  Artix rolling | ||||
|     bootloaderEntryName: Artix | ||||
|     productUrl:          https://www.artixlinux.org/ | ||||
|     supportUrl:          https://github.com/calamares/calamares/issues | ||||
|     knownIssuesUrl:      https://calamares.io/about/ | ||||
|     releaseNotesUrl:     https://calamares.io/about/ | ||||
| #     donateUrl:           https://kde.org/community/donations/index.php | ||||
|  | ||||
| # These images are loaded from the branding module directory. | ||||
| # | ||||
| # productBanner is an optional image, which if present, will be shown | ||||
| #       on the welcome page of the application, above the welcome text. | ||||
| #       It is intended to have a width much greater than height. | ||||
| #       It is displayed at 64px height (also on HiDPI). | ||||
| #       Recommended size is 64px tall, and up to 460px wide. | ||||
| # productIcon is used as the window icon, and will (usually) be used | ||||
| #       by the window manager to represent the application. This image | ||||
| #       should be square, and may be displayed by the window manager | ||||
| #       as small as 16x16 (but possibly larger). | ||||
| # productLogo is used as the logo at the top of the left-hand column | ||||
| #       which shows the steps to be taken. The image should be square, | ||||
| #       and is displayed at 80x80 pixels (also on HiDPI). | ||||
| # productWallpaper is an optional image, which if present, will replace | ||||
| #       the normal solid background on every page of the application. | ||||
| #       It can be any size and proportion, | ||||
| #       and will be tiled to fit the entire window. | ||||
| #       For a non-tiled wallpaper, the size should be the same as | ||||
| #       the overall window, see *windowSize* above (800x520). | ||||
| # productWelcome is shown on the welcome page of the application in | ||||
| #       the middle of the window, below the welcome text. It can be | ||||
| #       any size and proportion, and will be scaled to fit inside | ||||
| #       the window. Use `welcomeExpandingLogo` to make it non-scaled. | ||||
| #       Recommended size is 320x150. | ||||
| # | ||||
| # These filenames can also use substitutions from os-release (see above). | ||||
| images: | ||||
|     productBanner:       "banner.png" | ||||
|     productIcon:         "logo.png" | ||||
|     productLogo:         "logo.png" | ||||
|     # productWallpaper:    "wallpaper.png" | ||||
|     productWelcome:      "languages.png" | ||||
|  | ||||
| # Colors for text and background components. | ||||
| # | ||||
| #  - SidebarBackground is the background of the sidebar | ||||
| #  - SidebarText is the (foreground) text color | ||||
| #  - SidebarBackgroundCurrent sets the background of the current step. | ||||
| #    Optional, and defaults to the application palette. | ||||
| #  - SidebarTextCurrent is the text color of the current step. | ||||
| # | ||||
| # These colors can **also** be set through the stylesheet, if the | ||||
| # branding component also ships a stylesheet.qss. Then they are | ||||
| # the corresponding CSS attributes of #sidebarApp. | ||||
| style: | ||||
|     SidebarBackground:        "#292F34" | ||||
|     SidebarText:              "#FFFFFF" | ||||
|     SidebarTextCurrent:       "#292F34" | ||||
|     SidebarBackgroundCurrent: "#16a3f5" | ||||
|  | ||||
| ### SLIDESHOW | ||||
| # | ||||
| # The slideshow is displayed during execution steps (e.g. when the | ||||
| # installer is actually writing to disk and doing other slow things). | ||||
|  | ||||
| # The slideshow can be a QML file (recommended) which can display | ||||
| # arbitrary things -- text, images, animations, or even play a game -- | ||||
| # during the execution step. The QML **is** abruptly stopped when the | ||||
| # execution step is done, though, so maybe a game isn't a great idea. | ||||
| # | ||||
| # The slideshow can also be a sequence of images (not recommended unless | ||||
| # you don't want QML at all in your Calamares). The images are displayed | ||||
| # at a rate of 1 every 2 seconds during the execution step. | ||||
| # | ||||
| # To configure a QML file, list a single filename: | ||||
| #   slideshow:               "show.qml" | ||||
| # To configure images, like the filenames (here, as an inline list): | ||||
| #   slideshow: [ "/etc/calamares/slideshow/0.png", "/etc/logo.png" ] | ||||
| slideshow:               "show.qml" | ||||
|  | ||||
| # There are two available APIs for a QML slideshow: | ||||
| #  - 1 (the default) loads the entire slideshow when the installation- | ||||
| #      slideshow page is shown and starts the QML then. The QML | ||||
| #      is never stopped (after installation is done, times etc. | ||||
| #      continue to fire). | ||||
| #  - 2 loads the slideshow on startup and calls onActivate() and | ||||
| #      onLeave() in the root object. After the installation is done, | ||||
| #      the show is stopped (first by calling onLeave(), then destroying | ||||
| #      the QML components). | ||||
| # | ||||
| # An image slideshow does not need to have the API defined. | ||||
| slideshowAPI: 2 | ||||
|  | ||||
|  | ||||
| # These options are to customize online uploading of logs to pastebins: | ||||
| #  - type      : Defines the kind of pastebin service to be used. Currently | ||||
| #                it accepts two values: | ||||
| #                - none    :    disables the pastebin functionality | ||||
| #                - fiche   :    use fiche pastebin server | ||||
| #  - url       : Defines the address of pastebin service to be used. | ||||
| #                Takes string as input. Important bits are the host and port, | ||||
| #                the scheme is not used. | ||||
| #  - sizeLimit : Defines maximum size limit (in KiB) of log file to be pasted. | ||||
| #                The option must be set, to have the log option work. | ||||
| #                Takes integer as input. If < 0, no limit will be forced, | ||||
| #                else only last (approximately) 'n' KiB of log file will be pasted. | ||||
| #                Please note that upload size may be slightly over the limit (due | ||||
| #                to last minute logging), so provide a suitable value. | ||||
| uploadServer : | ||||
|     type :    "fiche" | ||||
|     url :     "http://termbin.com:9999" | ||||
|     sizeLimit : -1 | ||||
							
								
								
									
										
											BIN
										
									
								
								branding/artix/browsers.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 399 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/customise.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 818 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/desktops.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 151 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/languages.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 84 KiB | 
							
								
								
									
										2
									
								
								branding/artix/languages.png.license
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org> | ||||
| SPDX-License-Identifier: GPL-3.0-or-later | ||||
							
								
								
									
										
											BIN
										
									
								
								branding/artix/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/multimedia.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 363 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/office.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 359 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/packages1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 873 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/packages2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 MiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/productivity.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 502 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/rolling.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 121 KiB | 
							
								
								
									
										178
									
								
								branding/artix/show.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,178 @@ | ||||
| /* === This file is part of Calamares - <http://github.com/calamares> === | ||||
|  * | ||||
|  *   Copyright 2015, Teo Mrnjavac <teo@kde.org> | ||||
|  *   Copyright 2016, Luca Giambonini <almack@chakralinux.org> | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
|  *   the Free Software Foundation, either version 3 of the License, or | ||||
|  *   (at your option) any later version. | ||||
|  * | ||||
|  *   Calamares is distributed in the hope that it will be useful, | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *   GNU General Public License for more details. | ||||
|  * | ||||
|  *   You should have received a copy of the GNU General Public License | ||||
|  *   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| import QtQuick 2.0; | ||||
| import calamares.slideshow 1.0; | ||||
|  | ||||
| /* Tested with slide images of 1800x1200 and 1632x1248 pixels */ | ||||
|  | ||||
| Presentation | ||||
| { | ||||
|     id: presentation | ||||
|  | ||||
|     Timer { | ||||
|         interval: 10000 | ||||
|         running: true | ||||
|         repeat: true | ||||
|         onTriggered: presentation.goToNextSlide() | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic0 | ||||
|             source: "thanks.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic00 | ||||
|             source: "artix.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic02 | ||||
|             source: "tools.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic03 | ||||
|             source: "desktops.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic04 | ||||
|             source: "productivity.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic06 | ||||
|             source: "browsers.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic07 | ||||
|             source: "office.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic08 | ||||
|             source: "multimedia.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic09 | ||||
|             source: "web.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic11 | ||||
|             source: "packages1.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic13 | ||||
|             source: "packages2.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic15 | ||||
|             source: "customise.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Slide { | ||||
|         Image { | ||||
|             id: pic16 | ||||
|             source: "rolling.png" | ||||
|             width: parent.width * 1.12; height: parent.height * 1.5 | ||||
|             fillMode: Image.Stretch | ||||
|             anchors.centerIn: parent | ||||
|             anchors.verticalCenterOffset: -20 | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										96
									
								
								branding/artix/stylesheet.qss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,96 @@ | ||||
| /* | ||||
|  * SPDX-FileCopyrightText: no | ||||
|  * SPDX-License-Identifier: CC0-1.0 | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| A branding component can ship a stylesheet (like this one) | ||||
| which is applied to parts of the Calamares user-interface. | ||||
| In principle, all parts can be styled through CSS. | ||||
| Missing parts should be filed as issues. | ||||
|  | ||||
| The IDs are based on the object names in the C++ code. | ||||
| You can use the Debug Dialog to find out object names: | ||||
|   - Open the debug dialog | ||||
|   - Choose tab *Tools* | ||||
|   - Click *Widget Tree* button | ||||
| The list of object names is printed in the log. | ||||
|  | ||||
| Documentation for styling Qt Widgets through a stylesheet | ||||
| can be found at | ||||
|     https://doc.qt.io/qt-5/stylesheet-examples.html | ||||
|     https://doc.qt.io/qt-5/stylesheet-reference.html | ||||
| In Calamares, styling widget classes is supported (e.g. | ||||
| using `QComboBox` as a selector). | ||||
|  | ||||
| This example stylesheet has all the actual styling commented out. | ||||
| The examples are not exhaustive. | ||||
|  | ||||
| */ | ||||
|  | ||||
| /*** Generic Widgets. | ||||
|  * | ||||
|  * You can style **all** widgets of a given class by selecting | ||||
|  * the class name. Some widgets have specialized sub-selectors. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| QPushButton { background-color: green; } | ||||
| */ | ||||
|  | ||||
| /*** Main application window. | ||||
|  * | ||||
|  * The main application window has the sidebar, which in turn | ||||
|  * contains a logo and a list of items -- note that the list | ||||
|  * can **not** be styled, since it has its own custom C++ | ||||
|  * delegate code. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| #mainApp { } | ||||
| #sidebarApp { } | ||||
| #logoApp { } | ||||
| */ | ||||
|  | ||||
| /*** Welcome module. | ||||
|  * | ||||
|  * There are plenty of parts, but the buttons are the most interesting | ||||
|  * ones (donate, release notes, ...). The little icon image can be | ||||
|  * styled through *qproperty-icon*, which is a little obscure. | ||||
|  * URLs can reference the QRC paths of the Calamares application | ||||
|  * or loaded via plugins or within the filesystem. There is no | ||||
|  * comprehensive list of available icons, though. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| QPushButton#aboutButton { qproperty-icon: url(:/data/images/release.svg); } | ||||
| #donateButton, | ||||
| #supportButton, | ||||
| #releaseNotesButton, | ||||
| #knownIssuesButton { qproperty-icon: url(:/data/images/help.svg); } | ||||
| */ | ||||
|  | ||||
| /*** Partitioning module. | ||||
|  * | ||||
|  * Many moving parts, which you will need to experiment with. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| #bootInfoIcon { } | ||||
| #bootInfoLable { } | ||||
| #deviceInfoIcon { } | ||||
| #defineInfoLabel { } | ||||
| #scrollAreaWidgetContents { } | ||||
| #partitionBarView { } | ||||
| */ | ||||
|  | ||||
| /*** Licensing module. | ||||
|  * | ||||
|  * The licensing module paints individual widgets for each of | ||||
|  * the licenses. The item can be collapsed or expanded. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
| #licenseItem {  } | ||||
| #licenseItemFullText {  } | ||||
| */ | ||||
							
								
								
									
										
											BIN
										
									
								
								branding/artix/thanks.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 75 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/tools.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 160 KiB | 
							
								
								
									
										
											BIN
										
									
								
								branding/artix/web.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 136 KiB | 
							
								
								
									
										101
									
								
								branding/kaos_branding/about.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,101 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2020 2022 Anke Boersma <demm@kaosx.us> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.7 | ||||
| import QtQuick.Controls 2.0 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import QtQuick.Window 2.3 | ||||
|  | ||||
| ApplicationWindow { | ||||
|     id: about | ||||
|     visible: true | ||||
|     width: 760 | ||||
|     height: 400 | ||||
|     title: qsTr("About Calamares") | ||||
|  | ||||
|     property var appName: "Calamares" | ||||
|     property var appVersion: "3.3 RC" | ||||
|  | ||||
|     Rectangle { | ||||
|         id: textArea | ||||
|         anchors.fill: parent | ||||
|         color: "#f2f2f2" | ||||
|  | ||||
|         Column { | ||||
|             id: column | ||||
|             anchors.centerIn: parent | ||||
|  | ||||
|  | ||||
|             Rectangle { | ||||
|                 width: 560 | ||||
|                 height: 250 | ||||
|                 radius: 10 | ||||
|                 border.width: 0 | ||||
|  | ||||
|                 Text { | ||||
|                     width: 400 | ||||
|                     height: 250 | ||||
|                     anchors.centerIn: parent | ||||
|                     text: qsTr("<h1>%1</h1><br/> | ||||
|                         <strong>%2<br/> | ||||
|                         for %3</strong><br/><br/> | ||||
|                         Copyright 2014-2017 Teo Mrnjavac <teo@kde.org><br/> | ||||
|                         Copyright 2017-2022 Adriaan de Groot <groot@kde.org><br/> | ||||
|                         Thanks to <a href='https://calamares.io/team/'>the Calamares team</a> | ||||
|                         and the <a href=\"https://www.transifex.com/kaos/kaos/\">KaOS | ||||
|                         translators team</a>.<br/><br/> | ||||
|                         <a href='https://calamares.io/'>Calamares</a> | ||||
|                         development is sponsored by <br/> | ||||
|                         <a href='http://www.blue-systems.com/'>Blue Systems</a> - | ||||
|                         Liberating Software." ) | ||||
|                         .arg(appName) | ||||
|                         .arg(appVersion) | ||||
|                         .arg(Branding.string(Branding.VersionedName)) | ||||
|  | ||||
|                         onLinkActivated: Qt.openUrlExternally(link) | ||||
|  | ||||
|                         MouseArea { | ||||
|                             anchors.fill: parent | ||||
|                             acceptedButtons: Qt.NoButton | ||||
|                             cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor | ||||
|                         } | ||||
|  | ||||
|                     font.pointSize: 10 | ||||
|                     anchors.verticalCenterOffset: 10 | ||||
|                     anchors.horizontalCenterOffset: 40 | ||||
|                     wrapMode: Text.WordWrap | ||||
|                 } | ||||
|  | ||||
|                 Image { | ||||
|                     id: image | ||||
|                     x: 8 | ||||
|                     y: 12 | ||||
|                     height: 100 | ||||
|                     fillMode: Image.PreserveAspectFit | ||||
|                     source: "squid.png" | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         Button { | ||||
|             anchors.horizontalCenter: parent.horizontalCenter | ||||
|             anchors.bottom: parent.bottom | ||||
|             icon.name: "window-close" | ||||
|             text: qsTr("Close") | ||||
|             hoverEnabled: true | ||||
|             onClicked: about.close(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,22 +1,32 @@ | ||||
| --- | ||||
| componentName:  kaos | ||||
|  | ||||
| welcomeStyleCalamares:   false | ||||
|  | ||||
| # Should the welcome image (productWelcome, below) be scaled | ||||
| # up beyond its natural size? | ||||
| welcomeExpandingLogo:   true | ||||
|  | ||||
| windowExpanding:    normal | ||||
| windowSize: 920px,630px | ||||
| windowPlacement: center | ||||
|  | ||||
| sidebar: qml,bottom | ||||
| navigation: qml,right | ||||
|  | ||||
| strings: | ||||
|     productName:         KaOS | ||||
|     shortProductName:    KaOS | ||||
|     version:             2018.03 | ||||
|     version:             2022.08 | ||||
|     shortVersion:        KaOS | ||||
|     versionedName:       KaOS 2018.03 | ||||
|     versionedName:       KaOS 2022.08 | ||||
|     shortVersionedName:  KaOS 2018.03 | ||||
|     bootloaderEntryName: KaOS | ||||
|     productUrl:          https://kaosx.us/ | ||||
|     supportUrl:          https://kaosx.us/docs/ | ||||
|     knownIssuesUrl:      https://kaosx.us/pages/download/#known-issues | ||||
|     releaseNotesUrl:     https://kaosx.us/pages/release_notes | ||||
|     donateUrl:           https://kaosx.us/about/donors | ||||
|  | ||||
| images: | ||||
|     productLogo:         "kaos.png" | ||||
| @@ -27,6 +37,7 @@ slideshow:               "show.qml" | ||||
| slideshowAPI:            1 | ||||
|  | ||||
| style: | ||||
|    sidebarBackground:    "#bdc3c7" | ||||
|    sidebarText:          "#1F1F1F" | ||||
|    sidebarTextSelect:    "#3498DB" | ||||
|    SidebarBackground:    "#bdc3c7" | ||||
|    SidebarText:          "#1F1F1F" | ||||
|    SidebarTextCurrent:    "#3498DB" | ||||
|    SidebarBackgroundCurrent: "#eff0f1" | ||||
|   | ||||
							
								
								
									
										224
									
								
								branding/kaos_branding/calamares-navigation.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,224 @@ | ||||
| /* Sample of QML navigation. | ||||
|  | ||||
|    SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> | ||||
|    SPDX-FileCopyrightText: 2021 - 2022 Anke Boersma <demm@kaosx.us> | ||||
|    SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
|  | ||||
|    This navigation panel is for a "vertical" layout, with | ||||
|    mouse areas for next and previous and it includes the logo | ||||
|    plus About & Debug buttons. | ||||
| */ | ||||
| import io.calamares.ui 1.0 | ||||
| import io.calamares.core 1.0 | ||||
|  | ||||
| import QtQuick 2.3 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
|  | ||||
| Rectangle { | ||||
|     id: navigationBar; | ||||
|     color: Branding.styleString( Branding.SidebarBackground ); | ||||
|     height: parent.height; | ||||
|     width:64; | ||||
|  | ||||
|     ColumnLayout { | ||||
|         id: buttonBar | ||||
|         anchors.fill: parent; | ||||
|         spacing: 1 | ||||
|  | ||||
|         Image { | ||||
|             Layout.topMargin: 1; | ||||
|             Layout.bottomMargin:parent.height / 7; | ||||
|             Layout.alignment: Qt.AlignHCenter | Qt.AlignTop | ||||
|             id: logo; | ||||
|             width: 62; | ||||
|             height: width;  // square | ||||
|             source: "file:/" + Branding.imagePath(Branding.ProductLogo); | ||||
|             sourceSize.width: width; | ||||
|             sourceSize.height: height; | ||||
|         } | ||||
|  | ||||
|         Rectangle { | ||||
|             id: backArea | ||||
|             Layout.fillWidth: true; | ||||
|             Layout.preferredHeight: parent.height / 7; | ||||
|             color: mouseBack.containsMouse ? "#e6e9ea" : "#d9dcde"; | ||||
|             enabled: ViewManager.backEnabled; | ||||
|             visible: ViewManager.backAndNextVisible; | ||||
|  | ||||
|             MouseArea { | ||||
|                 id: mouseBack | ||||
|                 anchors.fill: parent; | ||||
|                 cursorShape: Qt.PointingHandCursor | ||||
|                 hoverEnabled: true | ||||
|  | ||||
|                 Text { | ||||
|                     anchors.centerIn: parent | ||||
|                     text: qsTr("Back") | ||||
|                     color: Branding.styleString( !backArea.enabled ? Branding.SidebarBackground : (mouseBack.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText )); | ||||
|                     font.pointSize : 8 | ||||
|                 } | ||||
|                 Image { | ||||
|                     source: "pan-start-symbolic.svg" | ||||
|                     anchors.centerIn: parent | ||||
|                     anchors.verticalCenterOffset : 18 | ||||
|                     fillMode: Image.PreserveAspectFit | ||||
|                     height: 32 | ||||
|                     opacity: backArea.enabled ? 1 : 0.2 | ||||
|                 } | ||||
|  | ||||
|                 onClicked: { ViewManager.back(); } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Rectangle { | ||||
|             id: nextArea | ||||
|             Layout.preferredHeight: parent.height / 7; | ||||
|             Layout.fillWidth: true | ||||
|             color: mouseNext.containsMouse ? "#f4f5f6" : "#e6e9ea"; | ||||
|             enabled: ViewManager.nextEnabled; | ||||
|             visible: ViewManager.backAndNextVisible; | ||||
|  | ||||
|             MouseArea { | ||||
|                 id: mouseNext | ||||
|                 anchors.fill: parent; | ||||
|                 cursorShape: Qt.PointingHandCursor | ||||
|                 hoverEnabled: true | ||||
|                 Text { | ||||
|                     anchors.centerIn: parent | ||||
|                     text: qsTr("Next") | ||||
|                     color: Branding.styleString( !nextArea.enabled ? Branding.SidebarBackground : (mouseNext.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText )); | ||||
|                     font.pointSize : 8 | ||||
|                 } | ||||
|                 Image { | ||||
|                     source: "pan-end-symbolic.svg" | ||||
|                     anchors.centerIn: parent | ||||
|                     anchors.verticalCenterOffset : 18 | ||||
|                     fillMode: Image.PreserveAspectFit | ||||
|                     height: 32 | ||||
|                     opacity: nextArea.enabled ? 1 : 0.2 | ||||
|                 } | ||||
|  | ||||
|                 onClicked: { ViewManager.next(); } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Rectangle { | ||||
|             id: cancelArea | ||||
|             height: parent.height / 7; | ||||
|             Layout.fillWidth: true | ||||
|             color: mouseCancel.containsMouse ? "#e6e9ea" : "#d9dcde"; | ||||
|  | ||||
|              /* | ||||
|              * The ViewManager has settings -- user-controlled via the | ||||
|              * branding component, and party based on program state -- | ||||
|              * whether the quit button should be enabled and visible. | ||||
|              * | ||||
|              * QML navigation *should* follow this pattern, but can also | ||||
|              * add other qualifications. For instance, you may have a | ||||
|              * "finished" module that handles quit in its own way, and | ||||
|              * want to hide the quit button then. The ViewManager has a | ||||
|              * current step and a total count, so compare them: | ||||
|              * | ||||
|              * visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1); | ||||
|              */ | ||||
|  | ||||
|              enabled: ViewManager.quitEnabled; | ||||
|              visible: ViewManager.quitVisible && ( ViewManager.currentStepIndex < ViewManager.rowCount()-1); | ||||
|  | ||||
|             ToolTip { | ||||
|                 width: 59 | ||||
|                 visible: mouseCancel.containsMouse | ||||
|                 timeout: 5000 | ||||
|                 delay: 1000 | ||||
|                 text: ViewManager.quitTooltip; | ||||
|             } | ||||
|  | ||||
|             MouseArea { | ||||
|                 id: mouseCancel | ||||
|                 anchors.fill: parent; | ||||
|                 cursorShape: Qt.PointingHandCursor | ||||
|                 hoverEnabled: true | ||||
|                 Text { | ||||
|                     anchors.centerIn: parent | ||||
|                     text: qsTr("Cancel") | ||||
|                     color: Branding.styleString( !cancelArea.enabled ? Branding.SidebarBackground : (mouseCancel.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarText )); | ||||
|                     font.pointSize : 8 | ||||
|                 } | ||||
|                 Image { | ||||
|                     source: "draw-rectangle.svg" | ||||
|                     anchors.centerIn: parent | ||||
|                     anchors.verticalCenterOffset : 18 | ||||
|                     fillMode: Image.PreserveAspectFit | ||||
|                     height: 9 | ||||
|                     opacity: cancelArea.enabled ? 1 : 0.2 | ||||
|                 } | ||||
|  | ||||
|                 onClicked: { ViewManager.quit(); } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Item { | ||||
|             Layout.fillHeight: true; | ||||
|         } | ||||
|  | ||||
|         Rectangle { | ||||
|             id: debugArea | ||||
|             Layout.fillWidth: true; | ||||
|             height: 35 | ||||
|             Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom | ||||
|             color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground); | ||||
|             visible: debug.enabled | ||||
|  | ||||
|             MouseArea { | ||||
|                 id: mouseAreaDebug | ||||
|                 anchors.fill: parent; | ||||
|                 cursorShape: Qt.PointingHandCursor | ||||
|                 hoverEnabled: true | ||||
|                 Text { | ||||
|                     anchors.centerIn: parent | ||||
|                     text: qsTr("Debug") | ||||
|                     color: Branding.styleString( mouseAreaDebug.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackground ); | ||||
|                     font.pointSize : 8 | ||||
|                 } | ||||
|  | ||||
|                 onClicked: debug.toggle() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Rectangle { | ||||
|             id: aboutArea | ||||
|             Layout.fillWidth: true; | ||||
|             height: 35 | ||||
|             Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom | ||||
|             color: Branding.styleString( mouseAreaAbout.containsMouse ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground); | ||||
|             visible: true | ||||
|  | ||||
|             MouseArea { | ||||
|                 id: mouseAreaAbout | ||||
|                 anchors.fill: parent; | ||||
|                 cursorShape: Qt.PointingHandCursor | ||||
|                 hoverEnabled: true | ||||
|                 Text { | ||||
|                     anchors.centerIn: parent | ||||
|                     text: qsTr("About") | ||||
|                     ToolTip { | ||||
|                         visible: mouseAreaAbout.containsMouse | ||||
|                         delay: 1000 | ||||
|                         text: qsTr("Info about Calamares") | ||||
|                     } | ||||
|                     color: Branding.styleString( mouseAreaAbout.containsMouse ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent ); | ||||
|                     font.pointSize : 8 | ||||
|                 } | ||||
|  | ||||
|                 property variant window; | ||||
|                 onClicked: { | ||||
|                     var component = Qt.createComponent("about.qml"); | ||||
|                     window = component.createObject(); | ||||
|                     window.show(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										72
									
								
								branding/kaos_branding/calamares-sidebar.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,72 @@ | ||||
| /* Sample of QML progress tree. | ||||
|  | ||||
|    SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> | ||||
|    SPDX-FileCopyrightText: 2021 - 2022 Anke Boersma <demm@kaosx.us> | ||||
|    SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
|  | ||||
|    The progress tree (actually a list) is "horizontal" in this example, | ||||
|    with the steps going to the right. | ||||
| */ | ||||
| import io.calamares.ui 1.0 | ||||
| import io.calamares.core 1.0 | ||||
|  | ||||
| import QtQuick 2.3 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import QtQuick.Controls 2.15 | ||||
|  | ||||
| Rectangle { | ||||
|     id: sideBar; | ||||
|     color: Branding.styleString( Branding.SidebarBackground ); | ||||
|     height: 48; | ||||
|     width: parent.width | ||||
|  | ||||
|     RowLayout { | ||||
|         anchors.fill: parent; | ||||
|         spacing: 2; | ||||
|  | ||||
|         Item { | ||||
|             Layout.fillHeight: true; | ||||
|         } | ||||
|  | ||||
|         Repeater { | ||||
|             model: ViewManager | ||||
|             Rectangle { | ||||
|                 Layout.leftMargin: 0; | ||||
|                 Layout.fillWidth: true; | ||||
|                 Layout.alignment: Qt.AlignTop; | ||||
|                 height: 42; | ||||
|                 radius: 0; | ||||
|                 color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarBackgroundCurrent : Branding.SidebarBackground ); | ||||
|  | ||||
|                 Text { | ||||
|                     anchors.verticalCenter: parent.verticalCenter; | ||||
|                     anchors.horizontalCenter: parent.horizontalCenter | ||||
|                     x: parent.x + 12; | ||||
|                     color: Branding.styleString( index == ViewManager.currentStepIndex ? Branding.SidebarTextCurrent : Branding.SidebarText ); | ||||
|                     text: display; | ||||
|                     font.pointSize : index == ViewManager.currentStepIndex ? 10 : 9 | ||||
|                 } | ||||
|  | ||||
|                 Rectangle { | ||||
|                     height: 2 | ||||
|                     width: 800 | ||||
|                     anchors.bottom: parent.bottom; | ||||
|                     border.color: Branding.styleString(ViewManager.currentStepIndex === index ? Branding.SidebarTextCurrent : (ViewManager.currentStepIndex >= index ? Branding.SidebarTextCurrent : Branding.SidebarBackgroundCurrent)) | ||||
|                     border.width: 3 | ||||
|  | ||||
|                     Image { | ||||
|                         source: "pan-up-symbolic.svg" | ||||
|                         id: image | ||||
|                         anchors.verticalCenter: parent.verticalCenter; | ||||
|                         anchors.verticalCenterOffset : -3 | ||||
|                         x: parent.x + 35; | ||||
|                         fillMode: Image.PreserveAspectFit | ||||
|                         height: 32 | ||||
|                         visible: index == ViewManager.currentStepIndex ? true : false | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										1
									
								
								branding/kaos_branding/draw-rectangle.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><path d="m2 1034.36v16h16v-16z" fill="#566060" transform="translate(1-1031.36)"/></svg> | ||||
| After Width: | Height: | Size: 147 B | 
| @@ -1,3 +1,6 @@ | ||||
| #   SPDX-FileCopyrightText: no | ||||
| #   SPDX-License-Identifier: CC0-1.0 | ||||
| # | ||||
| foreach( lang ast ca cs de en es es_AR fr hu id_ID nl_NL pl pt_BR pt_PT ro_RO ru sr_RS tr_TR zh_CN ) | ||||
|    list( APPEND TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/calamares-${COMPONENT_NAME}_${lang}.ts" ) | ||||
| endforeach() | ||||
|   | ||||
| Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 106 KiB | 
							
								
								
									
										15
									
								
								branding/kaos_branding/pan-end-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | ||||
| <?xml version='1.0' encoding='UTF-8' standalone='no'?> | ||||
| <svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new"> | ||||
|  <metadata id="metadata90"/> | ||||
|  <defs id="defs7386"> | ||||
|   <linearGradient id="linearGradient5606" osb:paint="solid"> | ||||
|    <stop id="stop5608"/> | ||||
|   </linearGradient> | ||||
|   <filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554"> | ||||
|    <feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/> | ||||
|   </filter> | ||||
|  </defs> | ||||
|  <g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-445.0002,-129)"> | ||||
|   <path inkscape:connector-curvature="0" d="m 451.0002,142 5,-5 -5,-5 z" id="path6412" sodipodi:nodetypes="cccc" fill="#555555"/> | ||||
|  </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 896 B | 
							
								
								
									
										15
									
								
								branding/kaos_branding/pan-start-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | ||||
| <?xml version='1.0' encoding='UTF-8' standalone='no'?> | ||||
| <svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new"> | ||||
|  <metadata id="metadata90"/> | ||||
|  <defs id="defs7386"> | ||||
|   <linearGradient id="linearGradient5606" osb:paint="solid"> | ||||
|    <stop id="stop5608"/> | ||||
|   </linearGradient> | ||||
|   <filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554"> | ||||
|    <feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/> | ||||
|   </filter> | ||||
|  </defs> | ||||
|  <g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-425.0002,-129)"> | ||||
|   <path inkscape:connector-curvature="0" d="m 435.0002,142 -5,-5 5,-5 z" id="path6400-8" sodipodi:nodetypes="cccc" fill="#555555"/> | ||||
|  </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 898 B | 
							
								
								
									
										15
									
								
								branding/kaos_branding/pan-up-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | ||||
| <?xml version='1.0' encoding='UTF-8' standalone='no'?> | ||||
| <svg height="16" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" version="1.1" width="16" xmlns="http://www.w3.org/2000/svg" enable-background="new"> | ||||
|  <metadata id="metadata90"/> | ||||
|  <defs id="defs7386"> | ||||
|   <linearGradient id="linearGradient5606" osb:paint="solid"> | ||||
|    <stop id="stop5608"/> | ||||
|   </linearGradient> | ||||
|   <filter inkscape:collect="always" color-interpolation-filters="sRGB" id="filter7554"> | ||||
|    <feBlend inkscape:collect="always" id="feBlend7556" in2="BackgroundImage" mode="darken"/> | ||||
|   </filter> | ||||
|  </defs> | ||||
|  <g inkscape:groupmode="layer" id="layer12" inkscape:label="actions" transform="translate(-465.0002,-129.00001)"> | ||||
|   <path inkscape:connector-curvature="0" d="m 478.0002,139 -5,-5 -5,5 z" id="path6418" sodipodi:nodetypes="cccc" fill="#3498DB"/> | ||||
|  </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 902 B | 
| @@ -1,6 +1,7 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   Copyright 2015, Teo Mrnjavac <teo@kde.org> | ||||
|  *   Copyright 2015-2018, Anke Boersma <demm@kaosx.us> | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
| @@ -24,7 +25,6 @@ Presentation | ||||
|     id: presentation | ||||
|  | ||||
|     Timer { | ||||
|         id: advanceTimer | ||||
|         interval: 5000 | ||||
|         running: false | ||||
|         repeat: true | ||||
| @@ -96,8 +96,8 @@ Presentation | ||||
|                 anchors.horizontalCenterOffset: -100 | ||||
|                 font.pixelSize: parent.width *.015 | ||||
|                 color: 'white' | ||||
|                 text: qsTr("The default Office Suite is Calligra.<br/>"+ | ||||
|                     "LibreOffice is available in the repositories. <br/>") | ||||
|                 text: qsTr("The default Office Suite is LibreOffice.<br/>"+ | ||||
|                     "Calligra is available in the repositories. <br/>") | ||||
|                 wrapMode: Text.WordWrap | ||||
|                 width: 450 | ||||
|                 horizontalAlignment: Text.AlignLeft | ||||
| @@ -120,7 +120,7 @@ Presentation | ||||
|                 font.pixelSize: parent.width *.015 | ||||
|                 color: 'white' | ||||
|                 text: qsTr("Qt/KDE specific internet applications include the <br/>"+ | ||||
|                     "Qupzilla web-browser and kde-telepathy for <br/>"+ | ||||
|                     "Falkon web-browser and kde-telepathy for <br/>"+ | ||||
|                     "chat and Instant Messaging. <br/>") | ||||
|                 wrapMode: Text.WordWrap | ||||
|                 width: 450 | ||||
| @@ -172,6 +172,4 @@ Presentation | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Component.onCompleted: advanceTimer.running = true | ||||
| } | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| # NOTE: this is largely a copy of the release script for Calamares, | ||||
| #       with not-applicable parts (such as translation-freeze) either | ||||
| #       commented-out, or skipped with if(false). | ||||
| # NOTE: this script contains Linuxisms (in particular, expects GNU mktemp(1)) | ||||
| # NOTE: this script may contain Linuxisms | ||||
| # | ||||
| # This attempts to perform the different steps of the RELEASE.md | ||||
| # document automatically. It's not tested on other machines or | ||||
| @@ -93,7 +93,7 @@ fi | ||||
| ### Setup | ||||
| # | ||||
| # | ||||
| BUILDDIR=$(mktemp -d --suffix=-build --tmpdir=.) | ||||
| BUILDDIR=$(mktemp -d -p . -t build.XXXXX) | ||||
|  | ||||
| ### Build with default compiler | ||||
| # | ||||
| @@ -140,30 +140,34 @@ fi | ||||
| ### Get version number for this release | ||||
| # | ||||
| # | ||||
| V=$( cd "$BUILDDIR" && make show-version | grep ^CALAMARES_VERSION | sed s/^[A-Z_]*=// ) | ||||
| V=$( cmake -DVERSION_STYLE=short -P CMakeLists.txt 2>&1 ) | ||||
| test -n "$V" || { echo "Could not obtain version in $BUILDDIR ." ; exit 1 ; } | ||||
|  | ||||
| ### Create signed tag | ||||
| # | ||||
| # This is the signing key ID associated with the GitHub account adriaandegroot, | ||||
| # which is used to create all "verified" tags in the Calamares repo. | ||||
| KEY_ID="CFDDC96F12B1915C" | ||||
| # | ||||
| KEY_ID="328D742D8807A435" | ||||
| git tag -u "$KEY_ID" -m "Release v$V" "v$V" || { echo "Could not sign tag v$V." ; exit 1 ; } | ||||
|  | ||||
| ### Create the tarball | ||||
| # | ||||
| # Create the tarball, compute SHA256 for later reporting, and | ||||
| # sign the tarball so the signature can be uploaded separately. | ||||
| # | ||||
| TAR_V="$TARBALL_PREFIX-$V" | ||||
| TAR_FILE="$TAR_V.tar.gz" | ||||
| git archive -o "$TAR_FILE" --prefix "$TAR_V/" "v$V" || { echo "Could not create tarball." ; exit 1 ; } | ||||
| test -f "$TAR_FILE" || { echo "Tarball was not created." ; exit 1 ; } | ||||
| SHA256=$(sha256sum "$TAR_FILE" | cut -d" " -f1) | ||||
| gpg -s -u $KEY_ID --detach --armor $TAR_FILE  # Sign the tarball | ||||
|  | ||||
| ### Build the tarball | ||||
| # | ||||
| # | ||||
| D=$(date +%Y%m%d-%H%M%S) | ||||
| TMPDIR=$(mktemp -d --suffix="-calamares-$D") | ||||
| TMPDIR=$(mktemp -d -p . -t calamares.XXXXX) | ||||
| test -d "$TMPDIR" || { echo "Could not create tarball-build directory." ; exit 1 ; } | ||||
| tar xzf "$TAR_FILE" -C "$TMPDIR" || { echo "Could not unpack tarball." ; exit 1 ; } | ||||
| test -d "$TMPDIR/$TAR_V" || { echo "Tarball did not contain source directory." ; exit 1 ; } | ||||
| @@ -183,7 +187,6 @@ rm -rf "$TMPDIR"  # From tarball | ||||
| cat <<EOF | ||||
| # Next steps for this release: | ||||
|   git push origin v$V | ||||
|   gpg -s -u $KEY_ID --detach --armor $TAR_FILE  # Sign the tarball | ||||
|   # Upload tarball $TAR_FILE and the signature $TAR_FILE.asc | ||||
|   # Announce via https://github.com/calamares/$PROJECT_NAME/releases/new | ||||
|   # SHA256: $SHA256 | ||||
|   | ||||
| @@ -5,12 +5,7 @@ | ||||
| #   SPDX-License-Identifier: BSD-2-Clause | ||||
| # | ||||
| # Calls astyle with settings matching Calamares coding style | ||||
| # Requires astyle >= 2.04 and clang-format-7 -8 or -9 | ||||
| # | ||||
| # Clang-format-10 is **not** supported, since it changes a default | ||||
| # that re-introduces a space into empty function bodies; this | ||||
| # can be turned off with a style setting, but that breaks | ||||
| # older format versions which don't recognize the setting. | ||||
| # Requires astyle >= 2.04 and clang-format-8 or later | ||||
| # | ||||
| # You can pass in directory names, in which case the files | ||||
| # in that directory (NOT below it) are processed. | ||||
| @@ -20,9 +15,16 @@ LC_ALL=C | ||||
| LC_NUMERIC=C | ||||
| export LANG LC_ALL LC_NUMERIC | ||||
|  | ||||
| BASEDIR=$(dirname $0) | ||||
| TOPDIR=$( cd $BASEDIR/.. && pwd -P ) | ||||
| test -d "$BASEDIR" || { echo "! Could not determine base for $0" ; exit 1 ; } | ||||
| test -d "$TOPDIR" || { echo "! Cound not determine top-level source dir" ; exit 1 ; } | ||||
| test -f "$TOPDIR/.clang-format.base" || { echo "! No .clang-format support files in $TOPDIR" ; exit 1 ; } | ||||
|  | ||||
| AS=$( which astyle ) | ||||
|  | ||||
| CF_VERSIONS="clang-format-7 clang-format-8 clang-format70 clang-format80 clang-format90 clang-format-9.0.1 clang-format" | ||||
| # Allow specifying CF_VERSIONS outside already | ||||
| CF_VERSIONS="$CF_VERSIONS clang-format-8 clang-format80 clang-format90 clang-format-9.0.1 clang-format" | ||||
| for _cf in $CF_VERSIONS | ||||
| do | ||||
| 	# Not an error if this particular clang-format isn't found | ||||
| @@ -35,14 +37,39 @@ test -n "$CF" || { echo "! No clang-format ($CF_VERSIONS) found in PATH"; exit 1 | ||||
| test -x "$AS" || { echo "! $AS is not executable."; exit 1 ; } | ||||
| test -x "$CF" || { echo "! $CF is not executable."; exit 1 ; } | ||||
|  | ||||
| unmangle_clang_format="" | ||||
| if expr `"$CF" --version | tr -dc '[^.0-9]' | cut  -d . -f 1` '<' 10 > /dev/null ; then | ||||
| 	: | ||||
| else | ||||
| 	unmangle_clang_format=$( dirname $0 )/../.clang-format | ||||
| 	echo "SpaceInEmptyBlock: false" >> "$unmangle_clang_format" | ||||
| fi | ||||
| ### CLANG-FORMAT-WRANGLING | ||||
| # | ||||
| # Version 7 and earlier doesn't understand all the options we would like | ||||
| # Version 8 is ok | ||||
| # Version 9 is ok | ||||
| # Later versions change some defaults so need extra wrangling. | ||||
| # .. there are extra files that are appended to the settings, per | ||||
| # .. clang-format version. | ||||
|  | ||||
| format_version=`"$CF" --version | tr -dc '[^.0-9]' | cut  -d . -f 1` | ||||
| case "$format_version" in | ||||
| 	[0-7] ) | ||||
| 		echo "! Clang-format version 8+ required" | ||||
| 		exit 1 | ||||
| 		;; | ||||
| 	[89] ) | ||||
| 		: | ||||
| 		;; | ||||
| 	* ) | ||||
| 		echo "! Clang-format version '$format_version' unsupported." | ||||
| 		exit 1 | ||||
| 		;; | ||||
| esac | ||||
| _fmt="$TOPDIR/.clang-format" | ||||
| cp "$_fmt.base" "$_fmt" | ||||
| for f in "$extra_settings" ; do | ||||
| 	test -f "$_fmt.$f" && cat "$_fmt.$f" >> "$_fmt" | ||||
| done | ||||
|  | ||||
|  | ||||
| ### FILE PROCESSING | ||||
| # | ||||
| # | ||||
| set -e | ||||
|  | ||||
| any_dirs=no | ||||
| @@ -54,7 +81,7 @@ done | ||||
| style_some() | ||||
| { | ||||
| 	if test -n "$*" ; then | ||||
| 		$AS --options=$(dirname $0)/astylerc --quiet "$@" | ||||
| 		$AS --options=$BASEDIR/astylerc --quiet "$@" | ||||
| 		$CF -i -style=file "$@" | ||||
| 	fi | ||||
| } | ||||
| @@ -72,6 +99,7 @@ else | ||||
| 	style_some "$@" | ||||
| fi | ||||
|  | ||||
| if test -n "$unmangle_clang_format" ; then | ||||
| 	sed -i.bak '/^SpaceInEmptyBlock/d' "$unmangle_clang_format" | ||||
| fi | ||||
| ### CLANG-FORMAT-WRANGLING | ||||
| # | ||||
| # Restore the original .clang-format | ||||
| cp "$_fmt.base" "$_fmt" | ||||
|   | ||||
							
								
								
									
										75
									
								
								modules/basestrap/basestrap.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,75 @@ | ||||
| # SPDX-FileCopyrightText: no | ||||
| # SPDX-License-Identifier: CC0-1.0 | ||||
| # | ||||
| # The configuration for the package manager starts with the | ||||
| # *backend* key, which picks one of the backends to use. | ||||
| # In `main.py` there is a base class `PackageManager`. | ||||
| # Implementations must subclass that and set a (class-level) | ||||
| # property *backend* to the name of the backend (e.g. "dummy"). | ||||
| # That property is used to match against the *backend* key here. | ||||
| # | ||||
| # You will have to add such a class for your package manager. | ||||
| # It is fairly simple Python code. The API is described in the | ||||
| # abstract methods in class `PackageManager`. Mostly, the only | ||||
| # trick is to figure out the correct commands to use, and in particular, | ||||
| # whether additional switches are required or not. Some package managers | ||||
| # have more installer-friendly defaults than others, e.g., DNF requires | ||||
| # passing --disablerepo=* -C to allow removing packages without Internet | ||||
| # connectivity, and it also returns an error exit code if the package did | ||||
| # not exist to begin with. | ||||
| --- | ||||
| # | ||||
| # Which package manager to use, options are: | ||||
| #  - pacman      - Pacman | ||||
| # | ||||
| # Not actually a package manager, but suitable for testing: | ||||
| #  - dummy       - Dummy manager, only logs | ||||
| # | ||||
| backend: dummy | ||||
|  | ||||
| # pacman specific options | ||||
| # | ||||
| # *num_retries* should be a positive integer which specifies the | ||||
| # number of times the call to pacman will be retried in the event of a | ||||
| # failure.  If it is missing, it will be set to 0. | ||||
| # | ||||
| # *disable_download_timeout* is a boolean that, when true, includes | ||||
| # the flag --disable-download-timeout on calls to pacman.  When missing, | ||||
| # false is assumed. | ||||
| # | ||||
| # *needed_only* is a boolean that includes the pacman argument --needed | ||||
| # when set to true.  If missing, false is assumed. | ||||
| # *handle_keyrings* is a boolean that includes initializing and populating keyrings | ||||
| # when set to true.  If missing, false is assumed. | ||||
|  | ||||
| pacman: | ||||
|     num_retries: 0 | ||||
|     disable_download_timeout: false | ||||
|     needed_only: false | ||||
|     handle_keyrings: false | ||||
|     copy_pacconf: false | ||||
|     requirements: | ||||
|       - name: /etc | ||||
|         mode: "0o755" | ||||
|       - name: /var | ||||
|         mode: "0o755" | ||||
|       - name: /var/cache | ||||
|         mode: "0o755" | ||||
|       - name: /var/cache/pacman | ||||
|         mode: "0o755" | ||||
|       - name: /var/cache/pacman/pkg | ||||
|         mode: "0o755" | ||||
|       - name: /var/lib | ||||
|         mode: "0o755" | ||||
|       - name: /var/lib/pacman | ||||
|         mode: "0o755" | ||||
|     keyrings: | ||||
|       - artix | ||||
|  | ||||
| # the artix base package allows selection of the init system tied to elogind | ||||
| # this option is artix specific | ||||
| # base_init: elogind | ||||
|  | ||||
| operations: | ||||
|   - install: | ||||
|     - base | ||||
							
								
								
									
										552
									
								
								modules/basestrap/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,552 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # === This file is part of Calamares - <https://calamares.io> === | ||||
| # | ||||
| #   SPDX-FileCopyrightText: 2014 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> | ||||
| #   SPDX-FileCopyrightText: 2015-2017 Teo Mrnjavac <teo@kde.org> | ||||
| #   SPDX-FileCopyrightText: 2016-2017 Kyle Robbertze <kyle@aims.ac.za> | ||||
| #   SPDX-FileCopyrightText: 2017 Alf Gaida <agaida@siduction.org> | ||||
| #   SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org> | ||||
| #   SPDX-FileCopyrightText: 2018 Philip Müller <philm@manjaro.org> | ||||
| #   SPDX-FileCopyrightText: 2023 Artoo <artoo@artixlinux.org> | ||||
| #   SPDX-License-Identifier: GPL-3.0-or-later | ||||
| # | ||||
| #   Calamares is Free Software: see the License-Identifier above. | ||||
| # | ||||
|  | ||||
| import abc | ||||
| from string import Template | ||||
| import os, shutil, subprocess, sys | ||||
|  | ||||
| import libcalamares | ||||
| from libcalamares.utils import host_env_process_output, target_env_process_output | ||||
| from libcalamares.utils import gettext_path, gettext_languages | ||||
| from os.path import join | ||||
|  | ||||
| import gettext | ||||
| _translation = gettext.translation("calamares-python", | ||||
|                                    localedir=gettext_path(), | ||||
|                                    languages=gettext_languages(), | ||||
|                                    fallback=True) | ||||
| _ = _translation.gettext | ||||
| _n = _translation.ngettext | ||||
|  | ||||
|  | ||||
| total_packages = 0  # For the entire job | ||||
| completed_packages = 0  # Done so far for this job | ||||
| group_packages = 0  # One group of packages from an -install or -remove entry | ||||
|  | ||||
| # A PM object may set this to a string (take care of translations!) | ||||
| # to override the string produced by pretty_status_message() | ||||
| custom_status_message = None | ||||
|  | ||||
| INSTALL = object() | ||||
| REMOVE = object() | ||||
| mode_packages = None  # Changes to INSTALL or REMOVE | ||||
|  | ||||
|  | ||||
| def _change_mode(mode): | ||||
|     global mode_packages | ||||
|     mode_packages = mode | ||||
|     libcalamares.job.setprogress(completed_packages * 1.0 / total_packages) | ||||
|  | ||||
|  | ||||
| def pretty_name(): | ||||
|     return _("Install packages.") | ||||
|  | ||||
|  | ||||
| def pretty_status_message(): | ||||
|     if custom_status_message is not None: | ||||
|         return custom_status_message | ||||
|     if not group_packages: | ||||
|         if (total_packages > 0): | ||||
|             # Outside the context of an operation | ||||
|             s = _("Processing packages (%(count)d / %(total)d)") | ||||
|         else: | ||||
|             s = _("Install packages.") | ||||
|  | ||||
|     elif mode_packages is INSTALL: | ||||
|         s = _n("Installing one package.", | ||||
|                "Installing %(num)d packages.", group_packages) | ||||
|     elif mode_packages is REMOVE: | ||||
|         s = _n("Removing one package.", | ||||
|                "Removing %(num)d packages.", group_packages) | ||||
|     else: | ||||
|         # No mode, generic description | ||||
|         s = _("Install packages.") | ||||
|  | ||||
|     return s % {"num": group_packages, | ||||
|                 "count": completed_packages, | ||||
|                 "total": total_packages} | ||||
|  | ||||
|  | ||||
|  | ||||
| class PackageManager(metaclass=abc.ABCMeta): | ||||
|     """ | ||||
|     Package manager base class. A subclass implements package management | ||||
|     for a specific backend, and must have a class property `backend` | ||||
|     with the string identifier for that backend. | ||||
|  | ||||
|     Subclasses are collected below to populate the list of possible | ||||
|     backends. | ||||
|     """ | ||||
|     backend = None | ||||
|  | ||||
|     @abc.abstractmethod | ||||
|     def install(self, pkgs, from_local=False): | ||||
|         """ | ||||
|         Install a list of packages (named) into the system. | ||||
|         Although this handles lists, in practice it is called | ||||
|         with one package at a time. | ||||
|  | ||||
|         @param pkgs: list[str] | ||||
|             list of package names | ||||
|         @param from_local: bool | ||||
|             if True, then these are local packages (on disk) and the | ||||
|             pkgs names are paths. | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     @abc.abstractmethod | ||||
|     def remove(self, pkgs): | ||||
|         """ | ||||
|         Removes packages. | ||||
|  | ||||
|         @param pkgs: list[str] | ||||
|             list of package names | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def run(self, script): | ||||
|         if script != "": | ||||
|             host_env_process_output(script.split(" ")) | ||||
|  | ||||
|     def install_package(self, packagedata, from_local=False): | ||||
|         """ | ||||
|         Install a package from a single entry in the install list. | ||||
|         This can be either a single package name, or an object | ||||
|         with pre- and post-scripts. If @p packagedata is a dict, | ||||
|         it is assumed to follow the documented structure. | ||||
|  | ||||
|         @param packagedata: str|dict | ||||
|         @param from_local: bool | ||||
|             see install.from_local | ||||
|         """ | ||||
|         if isinstance(packagedata, str): | ||||
|             self.install([packagedata], from_local=from_local) | ||||
|         else: | ||||
|             self.run(packagedata["pre-script"]) | ||||
|             self.install([packagedata["package"]], from_local=from_local) | ||||
|             self.run(packagedata["post-script"]) | ||||
|  | ||||
|     def remove_package(self, packagedata): | ||||
|         """ | ||||
|         Remove a package from a single entry in the remove list. | ||||
|         This can be either a single package name, or an object | ||||
|         with pre- and post-scripts. If @p packagedata is a dict, | ||||
|         it is assumed to follow the documented structure. | ||||
|  | ||||
|         @param packagedata: str|dict | ||||
|         """ | ||||
|         if isinstance(packagedata, str): | ||||
|             self.remove([packagedata]) | ||||
|         else: | ||||
|             self.run(packagedata["pre-script"]) | ||||
|             self.remove([packagedata["package"]]) | ||||
|             self.run(packagedata["post-script"]) | ||||
|  | ||||
|     def operation_install(self, package_list, from_local=False): | ||||
|         """ | ||||
|         Installs the list of packages named in @p package_list . | ||||
|         These can be strings -- plain package names -- or | ||||
|         structures (with a pre- and post-install step). | ||||
|  | ||||
|         This operation is called for "critical" packages, | ||||
|         which are expected to succeed, or fail, all together. | ||||
|         However, if there are packages with pre- or post-scripts, | ||||
|         then packages are installed one-by-one instead. | ||||
|  | ||||
|         NOTE: package managers may reimplement this method | ||||
|         NOTE: exceptions are expected to leave this method, to indicate | ||||
|               failure of the installation. | ||||
|         """ | ||||
|         if all([isinstance(x, str) for x in package_list]): | ||||
|             self.install(package_list, from_local=from_local) | ||||
|         else: | ||||
|             for package in package_list: | ||||
|                 self.install_package(package, from_local=from_local) | ||||
|  | ||||
|     def operation_try_install(self, package_list): | ||||
|         """ | ||||
|         Installs the list of packages named in @p package_list . | ||||
|         These can be strings -- plain package names -- or | ||||
|         structures (with a pre- and post-install step). | ||||
|  | ||||
|         This operation is called for "non-critical" packages, | ||||
|         which can succeed or fail without affecting the overall installation. | ||||
|         Packages are installed one-by-one to support package managers | ||||
|         that do not have a "install as much as you can" mode. | ||||
|  | ||||
|         NOTE: package managers may reimplement this method | ||||
|         NOTE: no package-installation exceptions should be raised | ||||
|         """ | ||||
|         # we make a separate package manager call for each package so a | ||||
|         # single failing package won't stop all of them | ||||
|         for package in package_list: | ||||
|             try: | ||||
|                 self.install_package(package) | ||||
|             except subprocess.CalledProcessError: | ||||
|                 libcalamares.utils.warning("Could not install package %s" % package) | ||||
|  | ||||
|     def operation_remove(self, package_list): | ||||
|         """ | ||||
|         Removes the list of packages named in @p package_list . | ||||
|         These can be strings -- plain package names -- or | ||||
|         structures (with a pre- and post-install step). | ||||
|  | ||||
|         This operation is called for "critical" packages, which are | ||||
|         expected to succeed or fail all together. | ||||
|         However, if there are packages with pre- or post-scripts, | ||||
|         then packages are removed one-by-one instead. | ||||
|  | ||||
|         NOTE: package managers may reimplement this method | ||||
|         NOTE: exceptions should be raised to indicate failure | ||||
|         """ | ||||
|         if all([isinstance(x, str) for x in package_list]): | ||||
|             self.remove(package_list) | ||||
|         else: | ||||
|             for package in package_list: | ||||
|                 self.remove_package(package) | ||||
|  | ||||
|     def operation_try_remove(self, package_list): | ||||
|         """ | ||||
|         Same relation as try_install has to install, except it removes | ||||
|         packages instead. Packages are removed one-by-one. | ||||
|  | ||||
|         NOTE: package managers may reimplement this method | ||||
|         NOTE: no package-installation exceptions should be raised | ||||
|         """ | ||||
|         for package in package_list: | ||||
|             try: | ||||
|                 self.remove_package(package) | ||||
|             except subprocess.CalledProcessError: | ||||
|                 libcalamares.utils.warning("Could not remove package %s" % package) | ||||
|  | ||||
| ### PACKAGE MANAGER IMPLEMENTATIONS | ||||
| # | ||||
| # Keep these alphabetical (presumably both by class name and backend name), | ||||
| # even the Dummy implementation. | ||||
| # | ||||
|  | ||||
| class PMPacman(PackageManager): | ||||
|     backend = "pacman" | ||||
|  | ||||
|     def __init__(self): | ||||
|         import re | ||||
|         progress_match = re.compile("^\\((\\d+)/(\\d+)\\)") | ||||
|  | ||||
|         def line_cb(line): | ||||
|             if line.startswith(":: "): | ||||
|                 self.in_package_changes = "package" in line or "hooks" in line | ||||
|             else: | ||||
|                 if self.in_package_changes and line.endswith("...\n"): | ||||
|                     # Update the message, untranslated; do not change the | ||||
|                     # progress percentage, since there may be more "installing..." | ||||
|                     # lines in the output for the group, than packages listed | ||||
|                     # explicitly. We don't know how to calculate proper progress. | ||||
|                     global custom_status_message | ||||
|                     custom_status_message = "pacman: " + line.strip() | ||||
|                     libcalamares.job.setprogress(self.progress_fraction) | ||||
|             libcalamares.utils.debug(line) | ||||
|  | ||||
|         self.in_package_changes = False | ||||
|         self.line_cb = line_cb | ||||
|  | ||||
|         pacman = libcalamares.job.configuration.get("pacman", None) | ||||
|         if pacman is None: | ||||
|             pacman = dict() | ||||
|         if type(pacman) is not dict: | ||||
|             libcalamares.utils.warning("Job configuration *pacman* will be ignored.") | ||||
|             pacman = dict() | ||||
|         self.pacman_num_retries = pacman.get("num_retries", 0) | ||||
|         self.pacman_disable_timeout = pacman.get("disable_download_timeout", False) | ||||
|         self.pacman_needed_only = pacman.get("needed_only", False) | ||||
|         self.pacman_key = pacman.get("handle_keyrings", False) | ||||
|         self.pacman_pacconf = pacman.get("copy_pacconf", False) | ||||
|         self.pacman_requirements = pacman.get("requirements", []) | ||||
|         self.pacman_keyrings = pacman.get("keyrings", []) | ||||
|  | ||||
|     def reset_progress(self): | ||||
|         self.in_package_changes = False | ||||
|         # These are globals | ||||
|         self.progress_fraction = (completed_packages * 1.0 / total_packages) | ||||
|  | ||||
|     def run_pacman(self, command, callback=False): | ||||
|         """ | ||||
|         Call pacman in a loop until it is successful or the number of retries is exceeded | ||||
|         :param command: The pacman command to run | ||||
|         :param callback: An optional boolean that indicates if this pacman run should use the callback | ||||
|         :return: | ||||
|         """ | ||||
|  | ||||
|         pacman_count = 0 | ||||
|         while pacman_count <= self.pacman_num_retries: | ||||
|             pacman_count += 1 | ||||
|             try: | ||||
|                 if False: # callback: | ||||
|                     host_env_process_output(command, self.line_cb) | ||||
|                 else: | ||||
|                     host_env_process_output(command) | ||||
|  | ||||
|                 return | ||||
|             except subprocess.CalledProcessError: | ||||
|                 if pacman_count <= self.pacman_num_retries: | ||||
|                     pass | ||||
|                 else: | ||||
|                     raise | ||||
|  | ||||
|     def install(self, pkgs, from_local=False): | ||||
|  | ||||
|         install_root = libcalamares.globalstorage.value("rootMountPoint") | ||||
|  | ||||
|         cal_umask = os.umask(0) | ||||
|         for target in self.pacman_requirements: | ||||
|             dest = install_root + target["name"] | ||||
|             if not os.path.exists(dest): | ||||
|                 mod = int(target["mode"],8) | ||||
|                 os.mkdir(dest, mode=mod) | ||||
|                 libcalamares.utils.debug("Mode: {!s}".format(oct(mod))) | ||||
|                 libcalamares.utils.debug("Created: {!s}".format(dest)) | ||||
|  | ||||
|         path = join(install_root, "run") | ||||
|         os.chmod(path, 0o755) | ||||
|         os.umask(cal_umask) | ||||
|  | ||||
|         f = "etc/resolv.conf" | ||||
|         if os.path.exists(join("/",f)): | ||||
|             shutil.copy2(join("/",f), join(install_root, f)) | ||||
|  | ||||
|         command = ["pacman"] | ||||
|  | ||||
|         cachedir = join(install_root, "var/cache/pacman/pkg") | ||||
|         dbdir = join(install_root, "var/lib/pacman") | ||||
|         pacman_args = ["--root", install_root, "--dbpath", dbdir, "--cachedir", cachedir] | ||||
|         command.extend(pacman_args) | ||||
|  | ||||
|         # Don't ask for user intervention, take the default action | ||||
|         command.append("--noconfirm") | ||||
|  | ||||
|         # Don't report download progress for each file | ||||
|         command.append("--noprogressbar") | ||||
|  | ||||
|         if self.pacman_needed_only is True: | ||||
|             command.append("--needed") | ||||
|  | ||||
|         if self.pacman_disable_timeout is True: | ||||
|             command.append("--disable-download-timeout") | ||||
|  | ||||
|         if from_local: | ||||
|             command.append("-U") | ||||
|         else: | ||||
|             command.append("-Sy") | ||||
|  | ||||
|         command += pkgs | ||||
|  | ||||
|         libcalamares.utils.debug("Command: {!s}".format(command)) | ||||
|  | ||||
|         self.reset_progress() | ||||
|         self.run_pacman(command, True) | ||||
|  | ||||
|         if self.pacman_key: | ||||
|             self.init_keyring() | ||||
|             self.populate_keyring() | ||||
|  | ||||
|         if self.pacman_pacconf: | ||||
|             f = "etc/pacman.conf" | ||||
|             if os.path.exists(join("/",f)): | ||||
|                 shutil.copy2(join("/",f), join(install_root, f)) | ||||
|  | ||||
|     def remove(self, pkgs): | ||||
|         self.reset_progress() | ||||
|         self.run_pacman(["pacman", "-Rs", "--noconfirm"] + pkgs, True) | ||||
|  | ||||
|     def init_keyring(self): | ||||
|         target_env_process_output(["pacman-key", "--init"]) | ||||
|  | ||||
|     def populate_keyring(self): | ||||
|         target_env_process_output(["pacman-key", "--populate"] + self.pacman_keyrings) | ||||
|  | ||||
| # Collect all the subclasses of PackageManager defined above, | ||||
| # and index them based on the backend property of each class. | ||||
| backend_managers = [ | ||||
|     (c.backend, c) | ||||
|     for c in globals().values() | ||||
|     if type(c) is abc.ABCMeta and issubclass(c, PackageManager) and c.backend] | ||||
|  | ||||
|  | ||||
| def subst_locale(plist): | ||||
|     """ | ||||
|     Returns a locale-aware list of packages, based on @p plist. | ||||
|     Package names that contain LOCALE are localized with the | ||||
|     BCP47 name of the chosen system locale; if the system | ||||
|     locale is 'en' (e.g. English, US) then these localized | ||||
|     packages are dropped from the list. | ||||
|  | ||||
|     @param plist: list[str|dict] | ||||
|         Candidate packages to install. | ||||
|     @return: list[str|dict] | ||||
|     """ | ||||
|     locale = libcalamares.globalstorage.value("locale") | ||||
|     if not locale: | ||||
|         # It is possible to skip the locale-setting entirely. | ||||
|         # Then pretend it is "en", so that {LOCALE}-decorated | ||||
|         # package names are removed from the list. | ||||
|         locale = "en" | ||||
|  | ||||
|     ret = [] | ||||
|     for packagedata in plist: | ||||
|         if isinstance(packagedata, str): | ||||
|             packagename = packagedata | ||||
|         else: | ||||
|             packagename = packagedata["package"] | ||||
|  | ||||
|         # Update packagename: substitute LOCALE, and drop packages | ||||
|         # if locale is en and LOCALE is in the package name. | ||||
|         if locale != "en": | ||||
|             packagename = Template(packagename).safe_substitute(LOCALE=locale) | ||||
|         elif 'LOCALE' in packagename: | ||||
|             packagename = None | ||||
|  | ||||
|         if packagename is not None: | ||||
|             # Put it back in packagedata | ||||
|             if isinstance(packagedata, str): | ||||
|                 packagedata = packagename | ||||
|             else: | ||||
|                 packagedata["package"] = packagename | ||||
|  | ||||
|             ret.append(packagedata) | ||||
|  | ||||
|     return ret | ||||
|  | ||||
|  | ||||
| def run_operations(pkgman, entry): | ||||
|     """ | ||||
|     Call package manager with suitable parameters for the given | ||||
|     package actions. | ||||
|  | ||||
|     :param pkgman: PackageManager | ||||
|         This is the manager that does the actual work. | ||||
|     :param entry: dict | ||||
|         Keys are the actions -- e.g. "install" -- to take, and the values | ||||
|         are the (list of) packages to apply the action to. The actions are | ||||
|         not iterated in a specific order, so it is recommended to use only | ||||
|         one action per dictionary. The list of packages may be package | ||||
|         names (strings) or package information dictionaries with pre- | ||||
|         and post-scripts. | ||||
|     """ | ||||
|     global group_packages, completed_packages, mode_packages | ||||
|  | ||||
|     for key in entry.keys(): | ||||
|         package_list = subst_locale(entry[key]) | ||||
|         group_packages = len(package_list) | ||||
|         if key == "install": | ||||
|             _change_mode(INSTALL) | ||||
|             pkgman.operation_install(package_list) | ||||
|         elif key == "try_install": | ||||
|             _change_mode(INSTALL) | ||||
|             pkgman.operation_try_install(package_list) | ||||
|         elif key == "remove": | ||||
|             _change_mode(REMOVE) | ||||
|             pkgman.operation_remove(package_list) | ||||
|         elif key == "try_remove": | ||||
|             _change_mode(REMOVE) | ||||
|             pkgman.operation_try_remove(package_list) | ||||
|         elif key == "localInstall": | ||||
|             _change_mode(INSTALL) | ||||
|             pkgman.operation_install(package_list, from_local=True) | ||||
|         elif key == "source": | ||||
|             libcalamares.utils.debug("Package-list from {!s}".format(entry[key])) | ||||
|         else: | ||||
|             libcalamares.utils.warning("Unknown package-operation key {!s}".format(key)) | ||||
|         completed_packages += len(package_list) | ||||
|         libcalamares.job.setprogress(completed_packages * 1.0 / total_packages) | ||||
|         libcalamares.utils.debug("Pretty name: {!s}, setting progress..".format(pretty_name())) | ||||
|  | ||||
|     group_packages = 0 | ||||
|     _change_mode(None) | ||||
|  | ||||
|  | ||||
| def run(): | ||||
|     """ | ||||
|     Calls routine with detected package manager to install locale packages | ||||
|     or remove drivers not needed on the installed system. | ||||
|  | ||||
|     :return: | ||||
|     """ | ||||
|     global mode_packages, total_packages, completed_packages, group_packages | ||||
|  | ||||
|     backend = libcalamares.job.configuration.get("backend") | ||||
|  | ||||
|     for identifier, impl in backend_managers: | ||||
|         if identifier == backend: | ||||
|             pkgman = impl() | ||||
|             break | ||||
|         else: | ||||
|             return "Bad backend", "backend=\"{}\"".format(backend) | ||||
|  | ||||
|     if not libcalamares.globalstorage.value("hasInternet"): | ||||
|         libcalamares.utils.warning( "Package installation has been skipped: no internet" ) | ||||
|         return None | ||||
|  | ||||
|     operations = libcalamares.job.configuration.get("operations", []) | ||||
|     # if libcalamares.globalstorage.contains("packageOperations"): | ||||
|     #     operations += libcalamares.globalstorage.value("packageOperations") | ||||
|  | ||||
|     if libcalamares.job.configuration.get("base_init"): | ||||
|         base_init = libcalamares.job.configuration.get("base_init", None) | ||||
|  | ||||
|     if libcalamares.globalstorage.contains("netinstallAdd"): | ||||
|         data = libcalamares.globalstorage.value("netinstallAdd") | ||||
|         init_provider = data[0]["name"] | ||||
|         libcalamares.utils.debug("Init provider: {!s}".format(init_provider)) | ||||
|  | ||||
|         if base_init is not None: | ||||
|             init_pkg = "-".join([base_init, init_provider]) | ||||
|             libcalamares.utils.debug("Package added: {!s}".format(init_pkg)) | ||||
|             operations[0]["install"].append(init_pkg) | ||||
|  | ||||
|     if init_provider is not None: | ||||
|         libcalamares.globalstorage.insert("initProvider", init_provider) | ||||
|  | ||||
|     libcalamares.globalstorage.insert("packageOperationsBasestrap", operations) | ||||
|  | ||||
|     mode_packages = None | ||||
|     total_packages = 0 | ||||
|     completed_packages = 0 | ||||
|     for op in operations: | ||||
|         for packagelist in op.values(): | ||||
|             total_packages += len(subst_locale(packagelist)) | ||||
|  | ||||
|     if not total_packages: | ||||
|         # Avoids potential divide-by-zero in progress reporting | ||||
|         return None | ||||
|  | ||||
|     for entry in operations: | ||||
|         group_packages = 0 | ||||
|         libcalamares.utils.debug(pretty_name()) | ||||
|         try: | ||||
|             run_operations(pkgman, entry) | ||||
|         except subprocess.CalledProcessError as e: | ||||
|             libcalamares.utils.warning(str(e)) | ||||
|             libcalamares.utils.debug("stdout:" + str(e.stdout)) | ||||
|             libcalamares.utils.debug("stderr:" + str(e.stderr)) | ||||
|             return (_("Package Manager error"), | ||||
|                     _("The package manager could not make changes to the installed system. The command <pre>{!s}</pre> returned error code {!s}.") | ||||
|                     .format(e.cmd, e.returncode)) | ||||
|  | ||||
|     mode_packages = None | ||||
|  | ||||
|     libcalamares.job.setprogress(1.0) | ||||
|  | ||||
|     return None | ||||
							
								
								
									
										7
									
								
								modules/basestrap/module.desc
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| # SPDX-FileCopyrightText: no | ||||
| # SPDX-License-Identifier: CC0-1.0 | ||||
| --- | ||||
| type:       "job" | ||||
| name:       "basestrap" | ||||
| interface:  "python" | ||||
| script:     "main.py" | ||||
| @@ -1,11 +0,0 @@ | ||||
| # The FileKeeper plugin preserves files from the live system | ||||
| # to the target system. It is particularly suited for copying | ||||
| # the log file(s) for post-installation or post-mortem examination. | ||||
|  | ||||
| calamares_add_plugin( filekeeper | ||||
|     TYPE job | ||||
|     EXPORT_MACRO PLUGINDLLEXPORT_PRO | ||||
|     SOURCES | ||||
|         FileKeeper.cpp | ||||
|     SHARED_LIB | ||||
| ) | ||||
| @@ -1,58 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   Copyright 2018, Adriaan de Groot <groot@kde.org> | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
|  *   the Free Software Foundation, either version 3 of the License, or | ||||
|  *   (at your option) any later version. | ||||
|  * | ||||
|  *   Calamares is distributed in the hope that it will be useful, | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *   GNU General Public License for more details. | ||||
|  * | ||||
|  *   You should have received a copy of the GNU General Public License | ||||
|  *   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include "FileKeeper.h" | ||||
|  | ||||
| #include <QDateTime> | ||||
| #include <QProcess> | ||||
| #include <QThread> | ||||
|  | ||||
| #include <GlobalStorage.h> | ||||
| #include <JobQueue.h> | ||||
|  | ||||
| #include <utils/Logger.h> | ||||
|  | ||||
| FileKeeperJob::FileKeeperJob( QObject* parent ) | ||||
|     : Calamares::CppJob( parent ) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| FileKeeperJob::~FileKeeperJob() {} | ||||
|  | ||||
|  | ||||
| QString | ||||
| FileKeeperJob::prettyName() const | ||||
| { | ||||
|     return tr( "File keeper Job" ); | ||||
| } | ||||
|  | ||||
| Calamares::JobResult | ||||
| FileKeeperJob::exec() | ||||
| { | ||||
|     return Calamares::JobResult::ok(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void | ||||
| FileKeeperJob::setConfigurationMap( const QVariantMap& configurationMap ) | ||||
| { | ||||
|     Q_UNUSED( configurationMap ); | ||||
| } | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DEFINITION( FileKeeperJobFactory, registerPlugin< FileKeeperJob >(); ) | ||||
| @@ -1,46 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   Copyright 2018, Adriaan de Groot <groot@kde.org> | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
|  *   the Free Software Foundation, either version 3 of the License, or | ||||
|  *   (at your option) any later version. | ||||
|  * | ||||
|  *   Calamares is distributed in the hope that it will be useful, | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *   GNU General Public License for more details. | ||||
|  * | ||||
|  *   You should have received a copy of the GNU General Public License | ||||
|  *   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #ifndef FILEKEEPER_H | ||||
| #define FILEKEEPER_H | ||||
|  | ||||
| #include <CppJob.h> | ||||
| #include <DllMacro.h> | ||||
| #include <utils/PluginFactory.h> | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QVariantMap> | ||||
|  | ||||
| class PLUGINDLLEXPORT FileKeeperJob : public Calamares::CppJob | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit FileKeeperJob( QObject* parent = nullptr ); | ||||
|     virtual ~FileKeeperJob() override; | ||||
|  | ||||
|     QString prettyName() const override; | ||||
|  | ||||
|     Calamares::JobResult exec() override; | ||||
|  | ||||
|     void setConfigurationMap( const QVariantMap& configurationMap ) override; | ||||
| }; | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DECLARATION( FileKeeperJobFactory ) | ||||
|  | ||||
| #endif | ||||
| @@ -1,14 +0,0 @@ | ||||
| if( NOT Calamares_WITH_QML ) | ||||
|     calamares_skip_module( "freebsddisk (QML is not supported in this build)" ) | ||||
|     return() | ||||
| endif() | ||||
|  | ||||
| calamares_add_plugin( freebsddisk | ||||
|     TYPE viewmodule | ||||
|     EXPORT_MACRO PLUGINDLLEXPORT_PRO | ||||
|     SOURCES | ||||
|         FreeBSDDiskViewStep.cpp | ||||
|     RESOURCES | ||||
|         freebsddisk.qrc | ||||
|     SHARED_LIB | ||||
| ) | ||||
| @@ -1,42 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
|  *   the Free Software Foundation, either version 3 of the License, or | ||||
|  *   (at your option) any later version. | ||||
|  * | ||||
|  *   Calamares is distributed in the hope that it will be useful, | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *   GNU General Public License for more details. | ||||
|  * | ||||
|  *   You should have received a copy of the GNU General Public License | ||||
|  *   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  *   License-Filename: LICENSES/GPL-3.0 | ||||
|  */ | ||||
|  | ||||
| #include "FreeBSDDiskViewStep.h" | ||||
|  | ||||
| FreeBSDDiskViewStep::FreeBSDDiskViewStep( QObject* parent ) | ||||
|     : Calamares::QmlViewStep( parent ) | ||||
| { | ||||
| } | ||||
|  | ||||
| FreeBSDDiskViewStep::~FreeBSDDiskViewStep() {} | ||||
|  | ||||
| QString | ||||
| FreeBSDDiskViewStep::prettyName() const | ||||
| { | ||||
|     return tr( "Disk Setup" ); | ||||
| } | ||||
|  | ||||
| void | ||||
| FreeBSDDiskViewStep::setConfigurationMap( const QVariantMap& configurationMap ) | ||||
| { | ||||
|     Calamares::QmlViewStep::setConfigurationMap( configurationMap );  // call parent implementation last | ||||
| } | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDDiskViewStepFactory, registerPlugin< FreeBSDDiskViewStep >(); ) | ||||
| @@ -1,44 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
|  *   the Free Software Foundation, either version 3 of the License, or | ||||
|  *   (at your option) any later version. | ||||
|  * | ||||
|  *   Calamares is distributed in the hope that it will be useful, | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *   GNU General Public License for more details. | ||||
|  * | ||||
|  *   You should have received a copy of the GNU General Public License | ||||
|  *   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  *   License-Filename: LICENSES/GPL-3.0 | ||||
|  */ | ||||
|  | ||||
|  | ||||
| #ifndef FREEBSDDISKVIEWSTEP_H | ||||
| #define FREEBSDDISKVIEWSTEP_H | ||||
|  | ||||
| #include "DllMacro.h" | ||||
| #include "utils/PluginFactory.h" | ||||
| #include "viewpages/QmlViewStep.h" | ||||
|  | ||||
| class PLUGINDLLEXPORT FreeBSDDiskViewStep : public Calamares::QmlViewStep | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     FreeBSDDiskViewStep( QObject* parent = nullptr ); | ||||
|     virtual ~FreeBSDDiskViewStep() override; | ||||
|  | ||||
|     QString prettyName() const override; | ||||
|  | ||||
|     void setConfigurationMap( const QVariantMap& configurationMap ) override; | ||||
| }; | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDDiskViewStepFactory ) | ||||
|  | ||||
| #endif | ||||
| @@ -1,7 +0,0 @@ | ||||
| # The *freebsddisk* module can be used to pick a disk | ||||
| # as an installer step. This module supports ZFSroot | ||||
| # on one whole disk, and UFSroot on one whole disk. | ||||
| # | ||||
| --- | ||||
| qmlSearch: both | ||||
|  | ||||
| @@ -1,35 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   Calamares is free software: you can redistribute it and/or modify | ||||
|  *   it under the terms of the GNU General Public License as published by | ||||
|  *   the Free Software Foundation, either version 3 of the License, or | ||||
|  *   (at your option) any later version. | ||||
|  * | ||||
|  *   Calamares is distributed in the hope that it will be useful, | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
|  *   GNU General Public License for more details. | ||||
|  * | ||||
|  *   You should have received a copy of the GNU General Public License | ||||
|  *   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
|  *  | ||||
|  *   SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  *   License-Filename: LICENSES/GPL-3.0 | ||||
|  */ | ||||
|  | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.7 | ||||
| import QtQuick.Controls 2.2 | ||||
| import QtQuick.Window 2.2 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import QtQuick.Controls.Material 2.1 | ||||
|  | ||||
| Item { | ||||
|     Text { | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 10 | ||||
|         text: "Select a disk on which to install FreeBSD." | ||||
|     } | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| <!DOCTYPE RCC><RCC version="1.0"> | ||||
| <qresource> | ||||
|   <file alias="freebsddisk.qml">freebsddisk.qml</file> | ||||
| </qresource> | ||||
| </RCC> | ||||
| @@ -1,15 +0,0 @@ | ||||
| # SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| calamares_add_plugin( mobile | ||||
|     TYPE viewmodule | ||||
|     EXPORT_MACRO PLUGINDLLEXPORT_PRO | ||||
|     SOURCES | ||||
|         Config.cpp | ||||
|         MobileQmlViewStep.cpp | ||||
|         PartitionJob.cpp | ||||
|         UsersJob.cpp | ||||
|     RESOURCES | ||||
|         mobile.qrc | ||||
|     SHARED_LIB | ||||
| ) | ||||
| @@ -1,198 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #include "Config.h" | ||||
| #include "PartitionJob.h" | ||||
| #include "UsersJob.h" | ||||
|  | ||||
| #include "ViewManager.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| #include <QVariant> | ||||
|  | ||||
| Config::Config( QObject* parent ) | ||||
|     : QObject( parent ) | ||||
| { | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setConfigurationMap( const QVariantMap& cfgMap ) | ||||
| { | ||||
|     using namespace CalamaresUtils; | ||||
|  | ||||
|     m_osName = getString( cfgMap, "osName", "(unknown)" ); | ||||
|     m_arch = getString( cfgMap, "arch", "(unknown)" ); | ||||
|     m_device = getString( cfgMap, "device", "(unknown)" ); | ||||
|     m_userInterface = getString( cfgMap, "userInterface", "(unknown)" ); | ||||
|     m_version = getString( cfgMap, "version", "(unknown)" ); | ||||
|     m_username = getString( cfgMap, "username", "user" ); | ||||
|     m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true ); | ||||
|  | ||||
|     m_builtinVirtualKeyboard = getBool( cfgMap, "builtinVirtualKeyboard", true ); | ||||
|  | ||||
|     m_featureSshd = getBool( cfgMap, "featureSshd", true ); | ||||
|     m_featureFsType = getBool( cfgMap, "featureFsType", false ); | ||||
|  | ||||
|     m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" ); | ||||
|     m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" ); | ||||
|     m_cmdMount = getString( cfgMap, "cmdMount", "mount" ); | ||||
|     m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" ); | ||||
|     m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" ); | ||||
|  | ||||
|     m_cmdMkfsRootBtrfs = getString( cfgMap, "cmdMkfsRootBtrfs", "mkfs.btrfs -L 'unknownOS_root'" ); | ||||
|     m_cmdMkfsRootExt4 = getString( cfgMap, "cmdMkfsRootExt4", "mkfs.ext4 -L 'unknownOS_root'" ); | ||||
|     m_cmdMkfsRootF2fs = getString( cfgMap, "cmdMkfsRootF2fs", "mkfs.f2fs -l 'unknownOS_root'" ); | ||||
|     m_fsList = getStringList( cfgMap, "fsModel", QStringList { "ext4", "f2fs", "btrfs" } ); | ||||
|     m_defaultFs = getString( cfgMap, "defaultFs", "ext4" ); | ||||
|     m_fsIndex = m_fsList.indexOf( m_defaultFs ); | ||||
|     m_fsType = m_defaultFs; | ||||
|  | ||||
|     m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" ); | ||||
|     m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" ); | ||||
|     m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" ); | ||||
|     m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" ); | ||||
|     m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" ); | ||||
| } | ||||
|  | ||||
| Calamares::JobList | ||||
| Config::createJobs() | ||||
| { | ||||
|     QList< Calamares::job_ptr > list; | ||||
|     QString cmdSshd = m_isSshEnabled ? m_cmdSshdEnable : m_cmdSshdDisable; | ||||
|  | ||||
|     /* Put users job in queue (should run after unpackfs) */ | ||||
|     Calamares::Job* j = new UsersJob( m_featureSshd, | ||||
|                                       m_cmdPasswd, | ||||
|                                       cmdSshd, | ||||
|                                       m_cmdSshdUseradd, | ||||
|                                       m_isSshEnabled, | ||||
|                                       m_username, | ||||
|                                       m_userPassword, | ||||
|                                       m_sshdUsername, | ||||
|                                       m_sshdPassword ); | ||||
|     list.append( Calamares::job_ptr( j ) ); | ||||
|  | ||||
|     return list; | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::runPartitionJobThenLeave( bool b ) | ||||
| { | ||||
|     Calamares::ViewManager* v = Calamares::ViewManager::instance(); | ||||
|     QString cmdMkfsRoot; | ||||
|     if ( m_fsType == QStringLiteral( "btrfs" ) ) | ||||
|     { | ||||
|         cmdMkfsRoot = m_cmdMkfsRootBtrfs; | ||||
|     } | ||||
|     else if ( m_fsType == QStringLiteral( "f2fs" ) ) | ||||
|     { | ||||
|         cmdMkfsRoot = m_cmdMkfsRootF2fs; | ||||
|     } | ||||
|     else if ( m_fsType == QStringLiteral( "ext4" ) ) | ||||
|     { | ||||
|         cmdMkfsRoot = m_cmdMkfsRootExt4; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         v->onInstallationFailed( "Unknown filesystem: '" + m_fsType + "'", "" ); | ||||
|     } | ||||
|     /* HACK: run partition job | ||||
|      * The "mobile" module has two jobs, the partition job and the users job. | ||||
|      * If we added both of them in Config::createJobs(), Calamares would run | ||||
|      * them right after each other. But we need the "unpackfs" module to run | ||||
|      * inbetween, that's why as workaround, the partition job is started here. | ||||
|      * To solve this properly, we would need to place the partition job in an | ||||
|      * own module and pass everything via globalstorage. But then we might as | ||||
|      * well refactor everything so we can unify the mobile's partition job with | ||||
|      * the proper partition job from Calamares. */ | ||||
|     Calamares::Job* j = new PartitionJob( m_cmdInternalStoragePrepare, | ||||
|                                           m_cmdLuksFormat, | ||||
|                                           m_cmdLuksOpen, | ||||
|                                           cmdMkfsRoot, | ||||
|                                           m_cmdMount, | ||||
|                                           m_targetDeviceRoot, | ||||
|                                           m_targetDeviceRootInternal, | ||||
|                                           m_installFromExternalToInternal, | ||||
|                                           m_isFdeEnabled, | ||||
|                                           m_fdePassword ); | ||||
|     Calamares::JobResult res = j->exec(); | ||||
|  | ||||
|     if ( res ) | ||||
|     { | ||||
|         v->next(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         v->onInstallationFailed( res.message(), res.details() ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setUserPassword( const QString& userPassword ) | ||||
| { | ||||
|     m_userPassword = userPassword; | ||||
|     emit userPasswordChanged( m_userPassword ); | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setSshdUsername( const QString& sshdUsername ) | ||||
| { | ||||
|     m_sshdUsername = sshdUsername; | ||||
|     emit sshdUsernameChanged( m_sshdUsername ); | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setSshdPassword( const QString& sshdPassword ) | ||||
| { | ||||
|     m_sshdPassword = sshdPassword; | ||||
|     emit sshdPasswordChanged( m_sshdPassword ); | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setIsSshEnabled( const bool isSshEnabled ) | ||||
| { | ||||
|     m_isSshEnabled = isSshEnabled; | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setFdePassword( const QString& fdePassword ) | ||||
| { | ||||
|     m_fdePassword = fdePassword; | ||||
|     emit fdePasswordChanged( m_fdePassword ); | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setIsFdeEnabled( const bool isFdeEnabled ) | ||||
| { | ||||
|     m_isFdeEnabled = isFdeEnabled; | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setInstallFromExternalToInternal( const bool val ) | ||||
| { | ||||
|     m_installFromExternalToInternal = val; | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setFsType( int idx ) | ||||
| { | ||||
|     if ( idx >= 0 && idx < m_fsList.length() ) | ||||
|     { | ||||
|         setFsType( m_fsList[ idx ] ); | ||||
|     } | ||||
| } | ||||
| void | ||||
| Config::setFsType( const QString& fsType ) | ||||
| { | ||||
|     if ( fsType != m_fsType ) | ||||
|     { | ||||
|         m_fsType = fsType; | ||||
|         emit fsTypeChanged( m_fsType ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setFsIndex( const int fsIndex ) | ||||
| { | ||||
|     m_fsIndex = fsIndex; | ||||
|     emit fsIndexChanged( m_fsIndex ); | ||||
| } | ||||
| @@ -1,194 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #pragma once | ||||
|  | ||||
| #include "Job.h" | ||||
|  | ||||
| #include <QObject> | ||||
| #include <memory> | ||||
|  | ||||
| class Config : public QObject | ||||
| { | ||||
|     Q_OBJECT | ||||
|     /* installer UI */ | ||||
|     Q_PROPERTY( bool builtinVirtualKeyboard READ builtinVirtualKeyboard CONSTANT FINAL ) | ||||
|  | ||||
|     /* welcome */ | ||||
|     Q_PROPERTY( QString osName READ osName CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString arch READ arch CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString device READ device CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString version READ version CONSTANT FINAL ) | ||||
|  | ||||
|     /* default user */ | ||||
|     Q_PROPERTY( QString username READ username CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged ) | ||||
|     Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL ) | ||||
|  | ||||
|     /* ssh server + credentials */ | ||||
|     Q_PROPERTY( bool featureSshd READ featureSshd CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString sshdUsername READ sshdUsername WRITE setSshdUsername NOTIFY sshdUsernameChanged ) | ||||
|     Q_PROPERTY( QString sshdPassword READ sshdPassword WRITE setSshdPassword NOTIFY sshdPasswordChanged ) | ||||
|     Q_PROPERTY( bool isSshEnabled READ isSshEnabled WRITE setIsSshEnabled ) | ||||
|  | ||||
|     /* full disk encryption */ | ||||
|     Q_PROPERTY( QString fdePassword READ fdePassword WRITE setFdePassword NOTIFY fdePasswordChanged ) | ||||
|     Q_PROPERTY( bool isFdeEnabled READ isFdeEnabled WRITE setIsFdeEnabled ) | ||||
|  | ||||
|     /* filesystem selection */ | ||||
|     Q_PROPERTY( QString fsType READ fsType WRITE setFsType NOTIFY fsTypeChanged ) | ||||
|     Q_PROPERTY( bool featureFsType READ featureFsType CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QStringList fsList READ fsList CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString defaultFs READ defaultFs CONSTANT FINAL ) | ||||
|     Q_PROPERTY( int fsIndex READ fsIndex WRITE setFsIndex NOTIFY fsIndexChanged ) | ||||
|  | ||||
|     /* partition job */ | ||||
|     Q_PROPERTY( bool runPartitionJobThenLeave READ runPartitionJobThenLeaveDummy WRITE runPartitionJobThenLeave ) | ||||
|     Q_PROPERTY( QString cmdInternalStoragePrepare READ cmdInternalStoragePrepare CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString cmdLuksFormat READ cmdLuksFormat CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString cmdLuksOpen READ cmdLuksOpen CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString cmdMount READ cmdMount CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString targetDeviceRoot READ targetDeviceRoot CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString targetDeviceRootInternal READ targetDeviceRootInternal CONSTANT FINAL ) | ||||
|     Q_PROPERTY( | ||||
|         bool installFromExternalToInternal READ installFromExternalToInternal WRITE setInstallFromExternalToInternal ) | ||||
|  | ||||
|     /* users job */ | ||||
|     Q_PROPERTY( QString cmdSshdEnable READ cmdSshdEnable CONSTANT FINAL ) | ||||
|     Q_PROPERTY( QString cmdSshdDisable READ cmdSshdDisable CONSTANT FINAL ) | ||||
|  | ||||
| public: | ||||
|     Config( QObject* parent = nullptr ); | ||||
|     void setConfigurationMap( const QVariantMap& ); | ||||
|     Calamares::JobList createJobs(); | ||||
|  | ||||
|     /* installer UI */ | ||||
|     bool builtinVirtualKeyboard() { return m_builtinVirtualKeyboard; } | ||||
|  | ||||
|     /* welcome */ | ||||
|     QString osName() const { return m_osName; } | ||||
|     QString arch() const { return m_arch; } | ||||
|     QString device() const { return m_device; } | ||||
|     QString userInterface() const { return m_userInterface; } | ||||
|     QString version() const { return m_version; } | ||||
|  | ||||
|     /* default user */ | ||||
|     QString username() const { return m_username; } | ||||
|     QString userPassword() const { return m_userPassword; } | ||||
|     void setUserPassword( const QString& userPassword ); | ||||
|     bool userPasswordNumeric() const { return m_userPasswordNumeric; } | ||||
|  | ||||
|     /* ssh server + credetials */ | ||||
|     bool featureSshd() { return m_featureSshd; } | ||||
|     QString sshdUsername() const { return m_sshdUsername; } | ||||
|     QString sshdPassword() const { return m_sshdPassword; } | ||||
|     bool isSshEnabled() { return m_isSshEnabled; } | ||||
|     void setSshdUsername( const QString& sshdUsername ); | ||||
|     void setSshdPassword( const QString& sshdPassword ); | ||||
|     void setIsSshEnabled( bool isSshEnabled ); | ||||
|  | ||||
|     /* full disk encryption */ | ||||
|     QString fdePassword() const { return m_fdePassword; } | ||||
|     bool isFdeEnabled() { return m_isFdeEnabled; } | ||||
|     void setFdePassword( const QString& fdePassword ); | ||||
|     void setIsFdeEnabled( bool isFdeEnabled ); | ||||
|  | ||||
|     /* filesystem selection */ | ||||
|     bool featureFsType() { return m_featureFsType; }; | ||||
|     QString fsType() const { return m_fsType; }; | ||||
|     void setFsType( int idx ); | ||||
|     void setFsType( const QString& fsType ); | ||||
|     QStringList fsList() const { return m_fsList; }; | ||||
|     int fsIndex() const { return m_fsIndex; }; | ||||
|     void setFsIndex( const int fsIndex ); | ||||
|     QString defaultFs() const { return m_defaultFs; }; | ||||
|  | ||||
|     /* partition job */ | ||||
|     bool runPartitionJobThenLeaveDummy() { return 0; } | ||||
|     void runPartitionJobThenLeave( bool b ); | ||||
|     QString cmdInternalStoragePrepare() const { return m_cmdInternalStoragePrepare; } | ||||
|     QString cmdLuksFormat() const { return m_cmdLuksFormat; } | ||||
|     QString cmdLuksOpen() const { return m_cmdLuksOpen; } | ||||
|     QString cmdMkfsRootBtrfs() const { return m_cmdMkfsRootBtrfs; } | ||||
|     QString cmdMkfsRootExt4() const { return m_cmdMkfsRootExt4; } | ||||
|     QString cmdMkfsRootF2fs() const { return m_cmdMkfsRootF2fs; } | ||||
|     QString cmdMount() const { return m_cmdMount; } | ||||
|     QString targetDeviceRoot() const { return m_targetDeviceRoot; } | ||||
|     QString targetDeviceRootInternal() const { return m_targetDeviceRootInternal; } | ||||
|     bool installFromExternalToInternal() { return m_installFromExternalToInternal; } | ||||
|     void setInstallFromExternalToInternal( const bool val ); | ||||
|  | ||||
|     /* users job */ | ||||
|     QString cmdPasswd() const { return m_cmdPasswd; } | ||||
|     QString cmdSshdEnable() const { return m_cmdSshdEnable; } | ||||
|     QString cmdSshdDisable() const { return m_cmdSshdDisable; } | ||||
|     QString cmdSshdUseradd() const { return m_cmdSshdUseradd; } | ||||
|  | ||||
| private: | ||||
|     /* installer UI */ | ||||
|     bool m_builtinVirtualKeyboard; | ||||
|  | ||||
|     /* welcome */ | ||||
|     QString m_osName; | ||||
|     QString m_arch; | ||||
|     QString m_device; | ||||
|     QString m_userInterface; | ||||
|     QString m_version; | ||||
|  | ||||
|     /* default user */ | ||||
|     QString m_username; | ||||
|     QString m_userPassword; | ||||
|     bool m_userPasswordNumeric; | ||||
|  | ||||
|     /* ssh server + credentials */ | ||||
|     bool m_featureSshd = false; | ||||
|     QString m_sshdUsername; | ||||
|     QString m_sshdPassword; | ||||
|     bool m_isSshEnabled = false; | ||||
|  | ||||
|     /* full disk encryption */ | ||||
|     QString m_fdePassword; | ||||
|     bool m_isFdeEnabled = false; | ||||
|  | ||||
|     /* filesystem selection */ | ||||
|     bool m_featureFsType = false; | ||||
|     QString m_defaultFs; | ||||
|     QString m_fsType; | ||||
|     // Index of the currently selected filesystem in UI. | ||||
|     int m_fsIndex = -1; | ||||
|     QStringList m_fsList; | ||||
|  | ||||
|     /* partition job */ | ||||
|     QString m_cmdInternalStoragePrepare; | ||||
|     QString m_cmdLuksFormat; | ||||
|     QString m_cmdLuksOpen; | ||||
|     QString m_cmdMkfsRootBtrfs; | ||||
|     QString m_cmdMkfsRootExt4; | ||||
|     QString m_cmdMkfsRootF2fs; | ||||
|     QString m_cmdMount; | ||||
|     QString m_targetDeviceRoot; | ||||
|     QString m_targetDeviceRootInternal; | ||||
|     bool m_installFromExternalToInternal = false; | ||||
|  | ||||
|     /* users job */ | ||||
|     QString m_cmdPasswd; | ||||
|     QString m_cmdSshdEnable; | ||||
|     QString m_cmdSshdDisable; | ||||
|     QString m_cmdSshdUseradd; | ||||
|  | ||||
| signals: | ||||
|     /* booleans we don't read from QML (like isSshEnabled) don't need a signal */ | ||||
|  | ||||
|     /* default user */ | ||||
|     void userPasswordChanged( QString userPassword ); | ||||
|  | ||||
|     /* ssh server + credentials */ | ||||
|     void sshdUsernameChanged( QString sshdUsername ); | ||||
|     void sshdPasswordChanged( QString sshdPassword ); | ||||
|  | ||||
|     /* full disk encryption */ | ||||
|     void fdePasswordChanged( QString fdePassword ); | ||||
|  | ||||
|     void fsTypeChanged( QString fsType ); | ||||
|     void fsIndexChanged( int fsIndex ); | ||||
| }; | ||||
| @@ -1,75 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #include "MobileQmlViewStep.h" | ||||
|  | ||||
| #include "GlobalStorage.h" | ||||
|  | ||||
| #include "locale/LabelModel.h" | ||||
| #include "utils/Dirs.h" | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| #include "Branding.h" | ||||
| #include "modulesystem/ModuleManager.h" | ||||
|  | ||||
| #include <QProcess> | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DEFINITION( MobileQmlViewStepFactory, registerPlugin< MobileQmlViewStep >(); ) | ||||
|  | ||||
| void | ||||
| MobileQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) | ||||
| { | ||||
|     m_config->setConfigurationMap( configurationMap ); | ||||
|     Calamares::QmlViewStep::setConfigurationMap( configurationMap ); | ||||
| } | ||||
|  | ||||
| MobileQmlViewStep::MobileQmlViewStep( QObject* parent ) | ||||
|     : Calamares::QmlViewStep( parent ) | ||||
|     , m_config( new Config( this ) ) | ||||
| { | ||||
| } | ||||
|  | ||||
| void | ||||
| MobileQmlViewStep::onLeave() | ||||
| { | ||||
|     return; | ||||
| } | ||||
|  | ||||
| bool | ||||
| MobileQmlViewStep::isNextEnabled() const | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| bool | ||||
| MobileQmlViewStep::isBackEnabled() const | ||||
| { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool | ||||
| MobileQmlViewStep::isAtBeginning() const | ||||
| { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool | ||||
| MobileQmlViewStep::isAtEnd() const | ||||
| { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| Calamares::JobList | ||||
| MobileQmlViewStep::jobs() const | ||||
| { | ||||
|     return m_config->createJobs(); | ||||
| } | ||||
|  | ||||
| QObject* | ||||
| MobileQmlViewStep::getConfig() | ||||
| { | ||||
|     return m_config; | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #ifndef PARTITION_QMLVIEWSTEP_H | ||||
| #define PARTITION_QMLVIEWSTEP_H | ||||
| #include "Config.h" | ||||
|  | ||||
| #include "utils/PluginFactory.h" | ||||
| #include "viewpages/QmlViewStep.h" | ||||
|  | ||||
| #include <DllMacro.h> | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QVariantMap> | ||||
|  | ||||
| class PLUGINDLLEXPORT MobileQmlViewStep : public Calamares::QmlViewStep | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit MobileQmlViewStep( QObject* parent = nullptr ); | ||||
|  | ||||
|     bool isNextEnabled() const override; | ||||
|     bool isBackEnabled() const override; | ||||
|     bool isAtBeginning() const override; | ||||
|     bool isAtEnd() const override; | ||||
|  | ||||
|     Calamares::JobList jobs() const override; | ||||
|  | ||||
|     void setConfigurationMap( const QVariantMap& configurationMap ) override; | ||||
|     void onLeave() override; | ||||
|     QObject* getConfig() override; | ||||
|  | ||||
| private: | ||||
|     Config* m_config; | ||||
| }; | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DECLARATION( MobileQmlViewStepFactory ) | ||||
|  | ||||
| #endif  // PARTITION_QMLVIEWSTEP_H | ||||
| @@ -1,136 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #include "PartitionJob.h" | ||||
|  | ||||
| #include "GlobalStorage.h" | ||||
| #include "JobQueue.h" | ||||
| #include "Settings.h" | ||||
| #include "utils/CalamaresUtilsSystem.h" | ||||
| #include "utils/Logger.h" | ||||
|  | ||||
| #include <QDir> | ||||
| #include <QFileInfo> | ||||
|  | ||||
|  | ||||
| PartitionJob::PartitionJob( const QString& cmdInternalStoragePrepare, | ||||
|                             const QString& cmdLuksFormat, | ||||
|                             const QString& cmdLuksOpen, | ||||
|                             const QString& cmdMkfsRoot, | ||||
|                             const QString& cmdMount, | ||||
|                             const QString& targetDeviceRoot, | ||||
|                             const QString& targetDeviceRootInternal, | ||||
|                             bool installFromExternalToInternal, | ||||
|                             bool isFdeEnabled, | ||||
|                             const QString& password ) | ||||
|     : Calamares::Job() | ||||
|     , m_cmdInternalStoragePrepare( cmdInternalStoragePrepare ) | ||||
|     , m_cmdLuksFormat( cmdLuksFormat ) | ||||
|     , m_cmdLuksOpen( cmdLuksOpen ) | ||||
|     , m_cmdMkfsRoot( cmdMkfsRoot ) | ||||
|     , m_cmdMount( cmdMount ) | ||||
|     , m_targetDeviceRoot( targetDeviceRoot ) | ||||
|     , m_targetDeviceRootInternal( targetDeviceRootInternal ) | ||||
|     , m_installFromExternalToInternal( installFromExternalToInternal ) | ||||
|     , m_isFdeEnabled( isFdeEnabled ) | ||||
|     , m_password( password ) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| QString | ||||
| PartitionJob::prettyName() const | ||||
| { | ||||
|     return "Creating and formatting installation partition"; | ||||
| } | ||||
|  | ||||
| /* Fill the "global storage", so the following jobs (like unsquashfs) work. | ||||
|    The code is similar to modules/partition/jobs/FillGlobalStorageJob.cpp in | ||||
|    Calamares. */ | ||||
| void | ||||
| FillGlobalStorage( const QString device, const QString pathMount ) | ||||
| { | ||||
|     using namespace Calamares; | ||||
|  | ||||
|     GlobalStorage* gs = JobQueue::instance()->globalStorage(); | ||||
|     QVariantList partitions; | ||||
|     QVariantMap partition; | ||||
|  | ||||
|     /* See mapForPartition() in FillGlobalStorageJob.cpp */ | ||||
|     partition[ "device" ] = device; | ||||
|     partition[ "mountPoint" ] = "/"; | ||||
|     partition[ "claimed" ] = true; | ||||
|  | ||||
|     /* Ignored by calamares modules used in combination with the "mobile" | ||||
|      * module, so we can get away with leaving them empty for now. */ | ||||
|     partition[ "uuid" ] = ""; | ||||
|     partition[ "fsName" ] = ""; | ||||
|     partition[ "fs" ] = ""; | ||||
|  | ||||
|     partitions << partition; | ||||
|     gs->insert( "partitions", partitions ); | ||||
|     gs->insert( "rootMountPoint", pathMount ); | ||||
| } | ||||
|  | ||||
| Calamares::JobResult | ||||
| PartitionJob::exec() | ||||
| { | ||||
|     using namespace Calamares; | ||||
|     using namespace CalamaresUtils; | ||||
|     using namespace std; | ||||
|  | ||||
|     const QString pathMount = "/mnt/install"; | ||||
|     const QString cryptName = "calamares_crypt"; | ||||
|     QString cryptDev = "/dev/mapper/" + cryptName; | ||||
|     QString passwordStdin = m_password + "\n"; | ||||
|     QString dev = m_targetDeviceRoot; | ||||
|     QList< QPair< QStringList, QString > > commands = {}; | ||||
|  | ||||
|     if ( m_installFromExternalToInternal ) | ||||
|     { | ||||
|         dev = m_targetDeviceRootInternal; | ||||
|  | ||||
|         commands.append( { | ||||
|             { { "sh", "-c", m_cmdInternalStoragePrepare }, QString() }, | ||||
|         } ); | ||||
|     } | ||||
|  | ||||
|     commands.append( { { { "mkdir", "-p", pathMount }, QString() } } ); | ||||
|  | ||||
|     if ( m_isFdeEnabled ) | ||||
|     { | ||||
|         commands.append( { | ||||
|             { { "sh", "-c", m_cmdLuksFormat + " " + dev }, passwordStdin }, | ||||
|             { { "sh", "-c", m_cmdLuksOpen + " " + dev + " " + cryptName }, passwordStdin }, | ||||
|             { { "sh", "-c", m_cmdMkfsRoot + " " + cryptDev }, QString() }, | ||||
|             { { "sh", "-c", m_cmdMount + " " + cryptDev + " " + pathMount }, QString() }, | ||||
|         } ); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         commands.append( { { { "sh", "-c", m_cmdMkfsRoot + " " + dev }, QString() }, | ||||
|                            { { "sh", "-c", m_cmdMount + " " + dev + " " + pathMount }, QString() } } ); | ||||
|     } | ||||
|  | ||||
|     foreach ( auto command, commands ) | ||||
|     { | ||||
|         const QStringList args = command.first; | ||||
|         const QString stdInput = command.second; | ||||
|         const QString pathRoot = "/"; | ||||
|  | ||||
|         ProcessResult res | ||||
|             = System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 120 ) ); | ||||
|         if ( res.getExitCode() ) | ||||
|         { | ||||
|             return JobResult::error( "Command failed:<br><br>" | ||||
|                                      "'" | ||||
|                                      + args.join( " " ) | ||||
|                                      + "'<br><br>" | ||||
|                                        " with output:<br><br>" | ||||
|                                        "'" | ||||
|                                      + res.getOutput() + "'" ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     FillGlobalStorage( m_isFdeEnabled ? cryptDev : dev, pathMount ); | ||||
|     return JobResult::ok(); | ||||
| } | ||||
| @@ -1,38 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #pragma once | ||||
| #include "Job.h" | ||||
|  | ||||
|  | ||||
| class PartitionJob : public Calamares::Job | ||||
| { | ||||
|     Q_OBJECT | ||||
| public: | ||||
|     PartitionJob( const QString& cmdInternalStoragePrepare, | ||||
|                   const QString& cmdLuksFormat, | ||||
|                   const QString& cmdLuksOpen, | ||||
|                   const QString& cmdMkfsRoot, | ||||
|                   const QString& cmdMount, | ||||
|                   const QString& targetDeviceRoot, | ||||
|                   const QString& targetDeviceRootInternal, | ||||
|                   bool installFromExternalToInternal, | ||||
|                   bool isFdeEnabled, | ||||
|                   const QString& password ); | ||||
|  | ||||
|     QString prettyName() const override; | ||||
|     Calamares::JobResult exec() override; | ||||
|  | ||||
|     Calamares::JobList createJobs(); | ||||
|  | ||||
| private: | ||||
|     QString m_cmdInternalStoragePrepare; | ||||
|     QString m_cmdLuksFormat; | ||||
|     QString m_cmdLuksOpen; | ||||
|     QString m_cmdMkfsRoot; | ||||
|     QString m_cmdMount; | ||||
|     QString m_targetDeviceRoot; | ||||
|     QString m_targetDeviceRootInternal; | ||||
|     bool m_installFromExternalToInternal; | ||||
|     bool m_isFdeEnabled; | ||||
|     QString m_password; | ||||
| }; | ||||
| @@ -1,88 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #include "UsersJob.h" | ||||
|  | ||||
| #include "GlobalStorage.h" | ||||
| #include "JobQueue.h" | ||||
| #include "Settings.h" | ||||
| #include "utils/CalamaresUtilsSystem.h" | ||||
| #include "utils/Logger.h" | ||||
|  | ||||
| #include <QDir> | ||||
| #include <QFileInfo> | ||||
|  | ||||
|  | ||||
| UsersJob::UsersJob( bool featureSshd, | ||||
|                     const QString& cmdPasswd, | ||||
|                     const QString& cmdSshd, | ||||
|                     const QString& cmdSshdUseradd, | ||||
|                     bool isSshEnabled, | ||||
|                     const QString& username, | ||||
|                     const QString& password, | ||||
|                     const QString& sshdUsername, | ||||
|                     const QString& sshdPassword ) | ||||
|     : Calamares::Job() | ||||
|     , m_featureSshd( featureSshd ) | ||||
|     , m_cmdPasswd( cmdPasswd ) | ||||
|     , m_cmdSshd( cmdSshd ) | ||||
|     , m_cmdSshdUseradd( cmdSshdUseradd ) | ||||
|     , m_isSshEnabled( isSshEnabled ) | ||||
|     , m_username( username ) | ||||
|     , m_password( password ) | ||||
|     , m_sshdUsername( sshdUsername ) | ||||
|     , m_sshdPassword( sshdPassword ) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| QString | ||||
| UsersJob::prettyName() const | ||||
| { | ||||
|     return "Configuring users"; | ||||
| } | ||||
|  | ||||
| Calamares::JobResult | ||||
| UsersJob::exec() | ||||
| { | ||||
|     using namespace Calamares; | ||||
|     using namespace CalamaresUtils; | ||||
|     using namespace std; | ||||
|  | ||||
|     QList< QPair< QStringList, QString > > commands = { | ||||
|         { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" }, | ||||
|     }; | ||||
|  | ||||
|     if ( m_featureSshd ) | ||||
|     { | ||||
|         commands.append( { { "sh", "-c", m_cmdSshd }, QString() } ); | ||||
|  | ||||
|         if ( m_isSshEnabled ) | ||||
|         { | ||||
|             commands.append( { { "sh", "-c", m_cmdSshdUseradd + " " + m_sshdUsername }, QString() } ); | ||||
|             commands.append( | ||||
|                 { { "sh", "-c", m_cmdPasswd + " " + m_sshdUsername }, m_sshdPassword + "\n" + m_sshdPassword + "\n" } ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     foreach ( auto command, commands ) | ||||
|     { | ||||
|         auto location = System::RunLocation::RunInTarget; | ||||
|         const QString pathRoot = "/"; | ||||
|         const QStringList args = command.first; | ||||
|         const QString stdInput = command.second; | ||||
|  | ||||
|         ProcessResult res = System::runCommand( location, args, pathRoot, stdInput, chrono::seconds( 30 ) ); | ||||
|         if ( res.getExitCode() ) | ||||
|         { | ||||
|             return JobResult::error( "Command failed:<br><br>" | ||||
|                                      "'" | ||||
|                                      + args.join( " " ) | ||||
|                                      + "'<br><br>" | ||||
|                                        " with output:<br><br>" | ||||
|                                        "'" | ||||
|                                      + res.getOutput() + "'" ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return JobResult::ok(); | ||||
| } | ||||
| @@ -1,36 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| #pragma once | ||||
| #include "Job.h" | ||||
|  | ||||
|  | ||||
| class UsersJob : public Calamares::Job | ||||
| { | ||||
|     Q_OBJECT | ||||
| public: | ||||
|     UsersJob( bool featureSshd, | ||||
|               const QString& cmdPasswd, | ||||
|               const QString& cmdSshd, | ||||
|               const QString& cmdSshdUseradd, | ||||
|               bool isSshEnabled, | ||||
|               const QString& username, | ||||
|               const QString& password, | ||||
|               const QString& sshdUsername, | ||||
|               const QString& sshdPassword ); | ||||
|  | ||||
|     QString prettyName() const override; | ||||
|     Calamares::JobResult exec() override; | ||||
|  | ||||
|     Calamares::JobList createJobs(); | ||||
|  | ||||
| private: | ||||
|     bool m_featureSshd; | ||||
|     QString m_cmdPasswd; | ||||
|     QString m_cmdSshd; | ||||
|     QString m_cmdSshdUseradd; | ||||
|     bool m_isSshEnabled; | ||||
|     QString m_username; | ||||
|     QString m_password; | ||||
|     QString m_sshdUsername; | ||||
|     QString m_sshdPassword; | ||||
| }; | ||||
| @@ -1,65 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: mainText | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         text: "To protect your data in case your device gets stolen," + | ||||
|               " it is recommended to enable full disk encryption.<br>" + | ||||
|               "<br>" + | ||||
|               "If you enable full disk encryption, you will be asked for" + | ||||
|               " a password. Without this password, it is not possible to" + | ||||
|               " boot your device or access any data on it. Make sure that" + | ||||
|               " you don't lose this password!" | ||||
|  | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         id: firstButton | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: mainText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Enable") | ||||
|         onClicked: { | ||||
|             config.isFdeEnabled = true; | ||||
|             navNext(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: firstButton.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Disable") | ||||
|         onClicked: { | ||||
|             config.isFdeEnabled = false; | ||||
|             navNextFeature(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,80 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     TextField { | ||||
|         id: password | ||||
|         anchors.top: parent.top | ||||
|         placeholderText: qsTr("Password") | ||||
|         inputMethodHints: Qt.ImhPreferLowercase | ||||
|         echoMode: TextInput.Password | ||||
|         onTextChanged: validatePassword(password, passwordRepeat, | ||||
|                                         errorText) | ||||
|         text: config.fdePassword | ||||
|  | ||||
|         onActiveFocusChanged: { | ||||
|             if(activeFocus) { | ||||
|                 Qt.inputMethod.update(Qt.ImQueryInput); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     TextField { | ||||
|         id: passwordRepeat | ||||
|         anchors.top: password.bottom | ||||
|         placeholderText: qsTr("Password (repeat)") | ||||
|         echoMode: TextInput.Password | ||||
|         onTextChanged: validatePassword(password, passwordRepeat, | ||||
|                                         errorText) | ||||
|         text: config.fdePassword | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Text { | ||||
|         id: errorText | ||||
|         anchors.top: passwordRepeat.bottom | ||||
|         visible: false | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|         wrapMode: Text.WordWrap | ||||
|     } | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: errorText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Continue") | ||||
|         onClicked: { | ||||
|             if (validatePassword(password, passwordRepeat, errorText)) { | ||||
|                 config.fdePassword = password.text; | ||||
|                 navNext(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,59 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Undef <calamares@undef.tools> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: mainText | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         text: "Select the filesystem for root partition. If unsure, leave the default." | ||||
|  | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     ComboBox { | ||||
|         id: fsTypeCB | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: mainText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|         height: 60 | ||||
|         editable: false | ||||
|         model: config.fsList | ||||
|         /* Save the current state on selection so it is there when the back button is pressed */ | ||||
|         onActivated: config.fsType = fsTypeCB.currentText; | ||||
|         Component.onCompleted: fsTypeCB.currentIndex = find( config.fsType, Qt.MatchContains ); | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: fsTypeCB.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Continue") | ||||
|         onClicked: { | ||||
|             config.fsType = fsTypeCB.currentText; | ||||
|             navNextFeature(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: mainText | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         text: (function() { | ||||
|             var ret = "Once you hit 'install', the installation will begin." + | ||||
|                 " It will typically take a few minutes. Do not power off the" + | ||||
|                 " device until it is done.<br><br>"; | ||||
|  | ||||
|             if (config.installFromExternalToInternal) { | ||||
|                 ret += "<b>After the installation, your device will shutdown" + | ||||
|                        " automatically. You must remove the external storage" + | ||||
|                        " (SD card) before booting again.</b>" + | ||||
|                        "<br><br>" + | ||||
|                        "Otherwise, your device will boot into the installer" + | ||||
|                        " again, and not into the installed system." | ||||
|             } else { | ||||
|                 ret += "Afterwards, it will reboot into the installed system."; | ||||
|             } | ||||
|  | ||||
|             return ret; | ||||
|         }()) | ||||
|  | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         id: firstButton | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: mainText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Install") | ||||
|         onClicked: navFinish() | ||||
|     } | ||||
| } | ||||
| @@ -1,64 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: mainText | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         text: "The installation was started from an external storage medium." + | ||||
|               "<br>" + | ||||
|               "You can either install to the same medium and overwrite the" + | ||||
|               " installer, or install to the internal storage.<br>" + | ||||
|               "<br>" + | ||||
|               "Where would you like to install " + config.osName + "?" | ||||
|  | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         id: firstButton | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: mainText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Internal (eMMC)") | ||||
|         onClicked: { | ||||
|             config.installFromExternalToInternal = true; | ||||
|             navNext(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: firstButton.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("External (SD card)") | ||||
|         onClicked: { | ||||
|             config.installFromExternalToInternal = false; | ||||
|             navNextFeature(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: mainText | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
| 	text: "Are you sure that you want to overwrite the internal storage?" + | ||||
| 	      "<br><br>" + | ||||
| 	      "<b>All existing data on the device will be lost!</b>" | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         id: firstButton | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: mainText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Yes") | ||||
|         onClicked: { | ||||
|             navNext(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: firstButton.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("No") | ||||
|         onClicked: { | ||||
|             navBack(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,125 +0,0 @@ | ||||
| # Commented out values are defaults. | ||||
| # All commands are running with 'sh -c'. | ||||
| --- | ||||
| ####### | ||||
| ### Target OS information | ||||
| ####### | ||||
|  | ||||
| ## Operating System Name | ||||
| # osName: "(unknown)" | ||||
|  | ||||
| ## User Interface name (e.g. Plasma Mobile) | ||||
| # userInterface: "(unknown)" | ||||
|  | ||||
| ## User Interface assumes that the password is numeric (as of writing, this is | ||||
| ## the case with Plasma Mobile and Phosh) | ||||
| # userPasswordNumeric: true | ||||
|  | ||||
| ## OS version | ||||
| # version: "(unknown)" | ||||
|  | ||||
| ## Default username (for which the password will be set) | ||||
| # username: "user" | ||||
|  | ||||
| ####### | ||||
| ### Target device information | ||||
| ####### | ||||
|  | ||||
| ## Architecture (e.g. aarch64) | ||||
| # arch: "(unknown)" | ||||
|  | ||||
| ## Name of the device (e.g. PinePhone) | ||||
| # device: "(unknown)" | ||||
|  | ||||
| ## Partition that will be formatted and mounted (optionally with FDE) for the | ||||
| ## rootfs | ||||
| # targetDeviceRoot: "/dev/unknown" | ||||
|  | ||||
| ## Partition that will be formatted and mounted (optionally with FDE) for the | ||||
| ## rootfs, on internal storage. The installer OS must not set this, if it was | ||||
| ## booted from the internal storage (this is not checked in the mobile | ||||
| ## module!). | ||||
| ## If this is set, the user gets asked whether they want to install on internal | ||||
| ## or external storage. If the user chose internal storage, | ||||
| ## cmdInternalStoragePrepare (see below) runs before this partition gets | ||||
| ## formatted (see below). A note is displayed, that the device is powered off | ||||
| ## after installation and that the user should remove the external storage | ||||
| ## medium. So you need to adjust the installer OS to poweroff in that case, and | ||||
| ## not reboot. See postmarketos-ondev.git for reference. | ||||
| # targetDeviceRootInternal: "" | ||||
|  | ||||
| ###### | ||||
| ### Installer Features | ||||
| ###### | ||||
|  | ||||
| ## Ask whether sshd should be enabled or not. If enabled, add a dedicated ssh | ||||
| ## user with proper username and password and suggest to change to key-based | ||||
| ## authentication after installation. | ||||
| # featureSshd: true | ||||
|  | ||||
| ## Ask the user, which filesystem to use. | ||||
| # featureFsType: false | ||||
| ## Filesystems that the user can choose from. | ||||
| #fsModel: | ||||
| #        - ext4 | ||||
| #        - f2fs | ||||
| #        - btrfs | ||||
| ## Default filesystem to display in the dialog. If featureFsType is disabled, | ||||
| ## this gets used without asking the user. | ||||
| # defaultFs: ext4 | ||||
|  | ||||
| ## Start Qt's virtual keyboard within the mobile module. Disable if you bring | ||||
| ## your own virtual keyboard (e.g. svkbd). | ||||
| # builtinVirtualKeyboard: true | ||||
|  | ||||
| ####### | ||||
| ### Commands running in the installer OS | ||||
| ####### | ||||
|  | ||||
| ## Format the target partition with LUKS | ||||
| ## Arguments: <device> | ||||
| ## Stdin: password with \n | ||||
| # cmdLuksFormat: "cryptsetup luksFormat --use-random" | ||||
|  | ||||
| ## Open the formatted partition | ||||
| ## Arguments: <device> <mapping name> | ||||
| ## Stdin: password with \n | ||||
| # cmdLuksOpen: "cryptsetup luksOpen" | ||||
|  | ||||
| ## Format the rootfs with a file system | ||||
| ## Arguments: <device> | ||||
| ## Btrfs: to allow snapshots to work on the root subvolume, it is recommended that this | ||||
| ## command be a script which will create a subvolume and make it default | ||||
| ## An example can be found at: | ||||
| ## https://gitlab.com/mobian1/calamares-settings-mobian/-/merge_requests/2/diffs#diff-content-dde34f5f1c89e3dea63608c553bbc452dedf428f | ||||
| # cmdMkfsRootBtrfs: "mkfs.btrfs -L 'unknownOS_root'" | ||||
| # cmdMkfsRootExt4: "mkfs.ext4 -L 'unknownOS_root'" | ||||
| # cmdMkfsRootF2fs: "mkfs.f2fs -l 'unknownOS_root'" | ||||
|  | ||||
| ## Mount the partition after formatting with file system | ||||
| ## Arguments: <device> <mountpoint> | ||||
| # cmdMount: "mount" | ||||
|  | ||||
| ## When user selects installation from external storage to internal storage | ||||
| ## (see targetDeviceRootInternal above), use this command to prepare the | ||||
| ## internal storage medium. The command must create a partition table with | ||||
| ## two partitions (boot, root) and fill the boot partition. See the | ||||
| ## ondev-internal-storage-prepare.sh in postmarketos-ondev as example. | ||||
| # cmdInternalStoragePrepare: "ondev-internal-storage-prepare" | ||||
|  | ||||
| ####### | ||||
| ### Commands running in the target OS (chroot) | ||||
| ####### | ||||
|  | ||||
| ## Set the password for default user and sshd user | ||||
| ## Arguments: <username> | ||||
| ## Stdin: password twice, each time with \n | ||||
| # cmdPasswd: "passwd" | ||||
|  | ||||
| ## Enable or disable sshd | ||||
| # cmdSshdEnable: "systemctl enable sshd.service" | ||||
| # cmdSshdDisable: "systemctl disable sshd.service" | ||||
|  | ||||
| ## Create the user for sshd | ||||
| ## Arguments: <username> | ||||
| # cmdSshdUseradd: "useradd -G wheel -m" | ||||
| @@ -1,423 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Page | ||||
| { | ||||
|     property var screen: "welcome" | ||||
|     property var screenPrevious: [] | ||||
|     property var titles: { | ||||
|         "welcome": null, /* titlebar disabled */ | ||||
|         "install_target": "Installation target", | ||||
|         "install_target_confirm": "Warning", | ||||
|         "user_pass": "User password", | ||||
|         "ssh_confirm": "SSH server", | ||||
|         "ssh_credentials": "SSH credentials", | ||||
|         "fs_selection": "Root filesystem", | ||||
|         "fde_confirm": "Full disk encryption", | ||||
|         "fde_pass": "Full disk encryption", | ||||
|         "install_confirm": "Ready to install", | ||||
|         "wait": null | ||||
|     } | ||||
|     property var features: [ | ||||
|         {"name": "welcome", | ||||
|          "screens": ["welcome"]}, | ||||
|         {"name": "installTarget", | ||||
|          "screens": ["install_target", "install_target_confirm"]}, | ||||
|         {"name": "userPassword", | ||||
|          "screens": ["user_pass"]}, | ||||
|         {"name": "sshd", | ||||
|          "screens": ["ssh_confirm", "ssh_credentials"]}, | ||||
|         {"name": "fsType", | ||||
|          "screens": ["fs_selection"]}, | ||||
|         {"name": "fde", | ||||
|          "screens": ["fde_confirm", "fde_pass"]}, | ||||
|         {"name": "installConfirm", | ||||
|          "screens": ["install_confirm", "wait"]} | ||||
|     ] | ||||
|     property var featureIdByScreen: (function() { | ||||
|         /* Put "features" above into an index of screen name -> feature id: | ||||
|          * featureIdByScreen = {"welcome": 0, "user_pass": 1, ...} */ | ||||
|         var ret = {}; | ||||
|         for (var i=0; i<features.length; i++) { | ||||
|             for (var j=0; j<features[i]["screens"].length; j++) { | ||||
|                 ret[ features[i]["screens"][j] ] = i; | ||||
|             } | ||||
|         } | ||||
|         return ret; | ||||
|     }()) | ||||
|     /* Only allow characters, that can be typed in with osk-sdl | ||||
|      * (src/keyboard.cpp). Details in big comment in validatePassword(). */ | ||||
|      property var allowed_chars: | ||||
|         /* layer 0 */ "abcdefghijklmnopqrstuvwxyz" + | ||||
|         /* layer 1 */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + | ||||
|         /* layer 2 */ "1234567890" + "@#$%&-_+()" + ",\"':;!?" + | ||||
|         /* layer 3 */ "~`|·√πτ÷×¶" + "©®£€¥^°*{}" + "\\/<>=[]" + | ||||
|         /* bottom row */ " ." | ||||
|  | ||||
|     Item { | ||||
|         id: appContainer | ||||
|         anchors.left: parent.left | ||||
|         anchors.top: parent.top | ||||
|         anchors.right: parent.right | ||||
|         anchors.bottom: inputPanel.top | ||||
|         Item { | ||||
|             width: parent.width | ||||
|             height: parent.height | ||||
|  | ||||
|             Rectangle { | ||||
|                 id: mobileNavigation | ||||
|                 width: parent.width | ||||
|                 height: 60 | ||||
|                 color: "#e6e4e1" | ||||
|                 Layout.fillWidth: true | ||||
|  | ||||
|                 border.width: 1 | ||||
|                 border.color: "#a7a7a7" | ||||
|  | ||||
|                 anchors.left: parent.left | ||||
|                 anchors.right: parent.right | ||||
|  | ||||
|                 RowLayout { | ||||
|                     width: parent.width | ||||
|                     height: parent.height | ||||
|                     spacing: 6 | ||||
|  | ||||
|                     Button { | ||||
|                         Layout.leftMargin: 6 | ||||
|                         id: mobileBack | ||||
|                         text: "<" | ||||
|  | ||||
|                         background: Rectangle { | ||||
|                             implicitWidth: 32 | ||||
|                             implicitHeight: 30 | ||||
|                             border.color: "#c1bab5" | ||||
|                             border.width: 1 | ||||
|                             radius: 4 | ||||
|                             color: mobileBack.down ? "#dbdbdb" : "#f2f2f2" | ||||
|                         } | ||||
|  | ||||
|                         onClicked: navBack() | ||||
|                     } | ||||
|                     Rectangle { | ||||
|                         implicitHeight: 30 | ||||
|                         Layout.fillWidth: true | ||||
|                         color: "#e6e4e1" | ||||
|  | ||||
|                         Text { | ||||
|                             id: mobileTitle | ||||
|                             text: "" | ||||
|                             color: "#303638" | ||||
|                             anchors.verticalCenter: parent.verticalCenter | ||||
|                             anchors.horizontalCenter: parent.horizontalCenter | ||||
|                         } | ||||
|                     } | ||||
|                     Rectangle { | ||||
|                         color: "#e6e4e1" | ||||
|                         Layout.rightMargin: 6 | ||||
|                         implicitWidth: 32 | ||||
|                         implicitHeight: 30 | ||||
|                         id: filler | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Loader { | ||||
|                 id: load | ||||
|                 anchors.left: parent.left | ||||
|                 anchors.top: mobileNavigation.bottom | ||||
|                 anchors.right: parent.right | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     InputPanel { | ||||
|         id: inputPanel | ||||
|         y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height | ||||
|         visible: config.builtinVirtualKeyboard | ||||
|         anchors.left: parent.left | ||||
|         anchors.right: parent.right | ||||
|     } | ||||
|  | ||||
|     Timer { | ||||
|         id: timer | ||||
|     } | ||||
|  | ||||
|     function skipFeatureInstallTarget() { | ||||
|         return config.targetDeviceRootInternal == ""; | ||||
|     } | ||||
|  | ||||
|     /* Navigation related */ | ||||
|     function navTo(name, historyPush=true) { | ||||
|         console.log("Navigating to screen: " + name); | ||||
|         if (historyPush) | ||||
|             screenPrevious.push(screen); | ||||
|         screen = name; | ||||
|         load.source = name + ".qml"; | ||||
|         mobileNavigation.visible = (titles[name] !== null); | ||||
|         mobileTitle.text = "<b>" + titles[name] + "</b>"; | ||||
|         Qt.inputMethod.hide(); | ||||
|     } | ||||
|     function navFinish() { | ||||
|         /* Show a waiting screen and wait a second (so it can render). The big | ||||
|          * comment in Config.cpp::runPartitionJobThenLeave() explains why this | ||||
|          * is necessary. */ | ||||
|         navTo("wait"); | ||||
|         timer.interval = 1000; | ||||
|         timer.repeat = false; | ||||
|         timer.triggered.connect(function() { | ||||
|             /* Trigger Config.cpp::runPartitionJobThenLeave(). (We could expose | ||||
|              * the function directly with qmlRegisterSingletonType somehow, but | ||||
|              * I haven't seen existing Calamares code do that with the Config | ||||
|              * object, so just use the side effect of setting the variable, as | ||||
|              * done in existing code of Calamares modules.) */ | ||||
|             config.runPartitionJobThenLeave = 1 | ||||
|         }); | ||||
|         timer.start(); | ||||
|     } | ||||
|     function navNextFeature() { | ||||
|         var id; | ||||
|  | ||||
|         /* Skip disabled features */ | ||||
|         for (id = featureIdByScreen[screen] + 1; id < features.length; id++) { | ||||
|             /* First letter uppercase */ | ||||
|             var name = features[id]["name"]; | ||||
|             var nameUp = name.charAt(0).toUpperCase() + name.slice(1); | ||||
|  | ||||
|             /* Check config.Feature<Name> */ | ||||
|             var configOption = "feature" + nameUp; | ||||
|             if (config[configOption] === false) { | ||||
|                 console.log("Skipping feature (disabled in config): " + name); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             /* Check skipFeature<Name>() */ | ||||
|             var funcName = "skipFeature" + nameUp; | ||||
|             if (eval("typeof " + funcName) === "function" | ||||
|                 && eval(funcName + "()")) { | ||||
|                 console.log("Skipping feature (skip function): " + name); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         console.log("Navigating to feature: " + features[id]["name"]); | ||||
|         return navTo(features[id]["screens"][0]); | ||||
|     } | ||||
|     function navNext() { | ||||
|         var featureId = featureIdByScreen[screen]; | ||||
|         var featureScreens = features[featureId]["screens"]; | ||||
|         for (var i = 0; i<featureScreens.length; i++) { | ||||
|             /* Seek ahead until i is current screen */ | ||||
|             if (featureScreens[i] != screen) | ||||
|                 continue; | ||||
|  | ||||
|             /* Navigate to next screen in same feature */ | ||||
|             if (i + 1 < featureScreens.length) { | ||||
|                 var screenNext = featureScreens[i + 1]; | ||||
|                 return navTo(screenNext); | ||||
|             } | ||||
|  | ||||
|             /* Screen is last in feature */ | ||||
|             return navNextFeature(); | ||||
|         } | ||||
|         console.log("ERROR: navNext() failed for screen: " + screen); | ||||
|     } | ||||
|     function navBack() { | ||||
|         if (screenPrevious.length) | ||||
|             return navTo(screenPrevious.pop(), false); | ||||
|         ViewManager.back(); | ||||
|     } | ||||
|     function onActivate() { | ||||
|         navTo(screen, false); | ||||
|     } | ||||
|  | ||||
|     /* Input validation: show/clear failures */ | ||||
|     function validationFailure(errorText, message="") { | ||||
|         errorText.text = message; | ||||
|         errorText.visible = true; | ||||
|         return false; | ||||
|     } | ||||
|     function validationFailureClear(errorText) { | ||||
|         errorText.text = ""; | ||||
|         errorText.visible = false; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /* Input validation: user-screens (user_pass, ssh_credentials) */ | ||||
|     function validatePin(userPin, userPinRepeat, errorText) { | ||||
|         var pin = userPin.text; | ||||
|         var repeat = userPinRepeat.text; | ||||
|  | ||||
|         if (pin == "") | ||||
|             return validationFailure(errorText); | ||||
|  | ||||
|         if (!pin.match(/^[0-9]*$/)) | ||||
|             return validationFailure(errorText, | ||||
|                                      "Only digits are allowed."); | ||||
|  | ||||
|         if (pin.length < 5) | ||||
|             return validationFailure(errorText, | ||||
|                                      "Too short: needs at least 5 digits."); | ||||
|  | ||||
|         if (repeat == "") | ||||
|             return validationFailure(errorText); | ||||
|  | ||||
|         if (repeat != pin) | ||||
|             return validationFailure(errorText, | ||||
|                                      "The PINs don't match."); | ||||
|  | ||||
|         return validationFailureClear(errorText); | ||||
|     } | ||||
|     function validateSshdUsername(username, errorText) { | ||||
|         var name = username.text; | ||||
|         var reserved = [ /* FIXME: make configurable */ | ||||
|             config.username, | ||||
|             "adm", | ||||
|             "at ", | ||||
|             "bin", | ||||
|             "colord", | ||||
|             "cron", | ||||
|             "cyrus", | ||||
|             "daemon", | ||||
|             "ftp", | ||||
|             "games", | ||||
|             "geoclue", | ||||
|             "guest", | ||||
|             "halt", | ||||
|             "lightdm", | ||||
|             "lp", | ||||
|             "mail", | ||||
|             "man", | ||||
|             "messagebus", | ||||
|             "news", | ||||
|             "nobody", | ||||
|             "ntp", | ||||
|             "operator", | ||||
|             "polkitd", | ||||
|             "postmaster", | ||||
|             "pulse", | ||||
|             "root", | ||||
|             "shutdown", | ||||
|             "smmsp", | ||||
|             "squid", | ||||
|             "sshd", | ||||
|             "sync", | ||||
|             "uucp", | ||||
|             "vpopmail", | ||||
|             "xfs", | ||||
|         ] | ||||
|  | ||||
|         /* Validate characters */ | ||||
|         for (var i=0; i<name.length; i++) { | ||||
|             if (i) { | ||||
|                 if (!name[i].match(/^[a-z0-9_-]$/)) | ||||
|                     return validationFailure(errorText, | ||||
|                                              "Characters must be lowercase" + | ||||
|                                              " letters, numbers,<br>" + | ||||
|                                              " underscores or minus signs."); | ||||
|             } else { | ||||
|                 if (!name[i].match(/^[a-z_]$/)) | ||||
|                     return validationFailure(errorText, | ||||
|                                              "First character must be a" + | ||||
|                                              " lowercase letter or an" + | ||||
|                                              " underscore."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /* Validate against reserved usernames */ | ||||
|         for (var i=0;i<reserved.length;i++) { | ||||
|             if (name == reserved[i]) | ||||
|                 return validationFailure(errorText, "Username '" + | ||||
|                                                     reserved[i] + | ||||
|                                                     "' is reserved.") | ||||
|         } | ||||
|  | ||||
|         /* Passed */ | ||||
|         return validationFailureClear(errorText); | ||||
|     } | ||||
|     function validateSshdPassword(password, passwordRepeat, errorText) { | ||||
|         var pass = password.text; | ||||
|         var repeat = passwordRepeat.text; | ||||
|  | ||||
|         if (pass == "") | ||||
|             return validationFailure(errorText); | ||||
|  | ||||
|         if (pass.length < 8) | ||||
|             return validationFailure(errorText, | ||||
|                                      "Too short: needs at least 8" + | ||||
|                                      " characters."); | ||||
|  | ||||
|         if (repeat == "") | ||||
|             return validationFailure(errorText); | ||||
|  | ||||
|         if (pass != repeat) | ||||
|             return validationFailure(errorText, "Passwords don't match."); | ||||
|  | ||||
|         return validationFailureClear(errorText); | ||||
|     } | ||||
|  | ||||
|     /* Input validation: fde_pass */ | ||||
|     function check_chars(input) { | ||||
|         for (var i = 0; i < input.length; i++) { | ||||
|             if (allowed_chars.indexOf(input[i]) == -1) | ||||
|                 return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     function allowed_chars_multiline() { | ||||
|         /* return allowed_chars split across multiple lines */ | ||||
|         var step = 20; | ||||
|         var ret = ""; | ||||
|         for (var i = 0; i < allowed_chars.length + step; i += step) | ||||
|             ret += allowed_chars.slice(i, i + step) + "\n"; | ||||
|         return ret.trim(); | ||||
|     } | ||||
|     function validatePassword(password, passwordRepeat, errorText) { | ||||
|         var pass = password.text; | ||||
|         var repeat = passwordRepeat.text; | ||||
|  | ||||
|         if (pass == "") | ||||
|             return validationFailure(errorText); | ||||
|  | ||||
|         /* This function gets called for the FDE password and for the user | ||||
|          * password. As of writing, all distributions shipping the mobile | ||||
|          * module are using osk-sdl to type in the FDE password after the | ||||
|          * installation, and another keyboard after booting up, to type in the | ||||
|          * user password. The osk-sdl password has the same keys as | ||||
|          * squeekboard's default layout, and other keyboards should be able to | ||||
|          * type these characters in as well. For now, verify that the password | ||||
|          * only contains characters that can be typed in by osk-sdl. If you | ||||
|          * need this to be more sophisticated, feel free to submit patches to | ||||
|          * make this more configurable. */ | ||||
|         if (!check_chars(pass)) | ||||
|             return validationFailure(errorText, | ||||
|                                      "The password must only contain" + | ||||
|                                      " these characters, others can possibly" + | ||||
|                                      " not be typed in after installation:\n" + | ||||
|                                      "\n" + | ||||
|                                      allowed_chars_multiline()); | ||||
|  | ||||
|         if (pass.length < 8) | ||||
|             return validationFailure(errorText, | ||||
|                                      "Too short: needs at least 8" + | ||||
|                                      " characters."); | ||||
|  | ||||
|         if (repeat == "") | ||||
|             return validationFailure(errorText); | ||||
|  | ||||
|         if (pass != repeat) | ||||
|             return validationFailure(errorText, "Passwords don't match."); | ||||
|  | ||||
|         return validationFailureClear(errorText); | ||||
|     } | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| <!DOCTYPE RCC><RCC version="1.0"> | ||||
| <qresource> | ||||
|   <file>mobile.qml</file> | ||||
|  | ||||
|   <file>welcome.qml</file> | ||||
|  | ||||
|   <file>install_target.qml</file> <!-- install from external to internal? --> | ||||
|   <file>install_target_confirm.qml</file> <!-- overwrite internal storage? --> | ||||
|  | ||||
|   <file>user_pass.qml</file> <!-- default user: password --> | ||||
|   <file>ssh_confirm.qml</file> <!-- sshd: enable or not? --> | ||||
|   <file>ssh_credentials.qml</file> <!-- sshd user: username, password --> | ||||
|   <file>fs_selection.qml</file> <!-- filesystem selection --> | ||||
|  | ||||
|   <file>fde_confirm.qml</file> <!-- enable FDE or not? --> | ||||
|   <file>fde_pass.qml</file> <!-- FDE password (optional) --> | ||||
|   <file>install_confirm.qml</file> <!-- final confirmation before install --> | ||||
|  | ||||
|   <file>wait.qml</file> <!-- please wait while partitioning --> | ||||
| </qresource> | ||||
| </RCC> | ||||
| @@ -1,68 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: mainText | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         text: "If you don't know what SSH is, choose 'disable'.<br>" + | ||||
|               "<br>" + | ||||
|               "With 'enable', you will be asked for a second username and" + | ||||
|               " password. You will be able to login to the SSH server with" + | ||||
|               " these credentials via USB (172.16.42.1), Wi-Fi and possibly" + | ||||
|               " cellular network. It is recommended to replace the password" + | ||||
|               " with an SSH key after the installation.<br>" + | ||||
|               "<br>" + | ||||
|               "More information:<br>" + | ||||
|               "https://postmarketos.org/ssh" | ||||
|  | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         id: firstButton | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: mainText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Enable") | ||||
|         onClicked: { | ||||
|             config.isSshEnabled = true; | ||||
|             navNext(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: firstButton.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Disable") | ||||
|         onClicked: { | ||||
|             config.isSshEnabled = false; | ||||
|             navNextFeature(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,107 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     TextField { | ||||
|         id: username | ||||
|         anchors.top: parent.top | ||||
|         placeholderText: qsTr("SSH username") | ||||
|         inputMethodHints: Qt.ImhPreferLowercase | ||||
|         onTextChanged: validateSshdUsername(username, errorTextUsername) | ||||
|         text: config.sshdUsername | ||||
|  | ||||
|         onActiveFocusChanged: { | ||||
|             if(activeFocus) { | ||||
|                 Qt.inputMethod.update(Qt.ImQueryInput); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Text { | ||||
|         id: errorTextUsername | ||||
|         anchors.top: username.bottom | ||||
|         visible: false | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     TextField { | ||||
|         id: password | ||||
|         anchors.top: errorTextUsername.bottom | ||||
|         placeholderText: qsTr("SSH password") | ||||
|         echoMode: TextInput.Password | ||||
|         onTextChanged: validateSshdPassword(password, passwordRepeat, | ||||
|                                            errorTextPassword) | ||||
|         text: config.sshdPassword | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     TextField { | ||||
|         id: passwordRepeat | ||||
|         anchors.top: password.bottom | ||||
|         placeholderText: qsTr("SSH password (repeat)") | ||||
|         echoMode: TextInput.Password | ||||
|         onTextChanged: validateSshdPassword(password, passwordRepeat, | ||||
|                                            errorTextPassword) | ||||
|         text: config.sshdPassword | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Text { | ||||
|         id: errorTextPassword | ||||
|         anchors.top: passwordRepeat.bottom | ||||
|         visible: false | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: errorTextPassword.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Continue") | ||||
|         onClicked: { | ||||
|             if (validateSshdUsername(username, errorTextUsername) && | ||||
|                 validateSshdPassword(password, passwordRepeat, | ||||
|                                     errorTextPassword)) { | ||||
|                 config.sshdUsername = username.text; | ||||
|                 config.sshdPassword = password.text; | ||||
|  | ||||
|                 navNext(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,114 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Item { | ||||
|     property var placeholder: (config.userPasswordNumeric | ||||
|                                ? "PIN" | ||||
|                                : "Password") | ||||
|     property var hints: (config.userPasswordNumeric | ||||
|                          ? Qt.ImhDigitsOnly | ||||
|                          : Qt.ImhPreferLowercase) | ||||
|     property var validateFunc: (config.userPasswordNumeric | ||||
|                                 ? validatePin | ||||
|                                 : validatePassword); | ||||
|  | ||||
|  | ||||
|     anchors.left: parent.left | ||||
|     anchors.top: parent.top | ||||
|     anchors.right: parent.right | ||||
|     width: parent.width | ||||
|     height: parent.height | ||||
|  | ||||
|     Text { | ||||
|         id: description | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: parent.top | ||||
|         anchors.topMargin: 30 | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         text: (function() { | ||||
|             if (config.userPasswordNumeric) { | ||||
|                 return "Set the numeric password of your user. The" + | ||||
|                        " lockscreen will ask for this PIN. This is" + | ||||
|                        " <i>not</i> the PIN of your SIM card. Make sure to" + | ||||
|                        " remember it."; | ||||
|             } else { | ||||
|                 return "Set the password of your user. The lockscreen will" + | ||||
|                       " ask for this password. Make sure to remember it."; | ||||
|             } | ||||
|         }()) | ||||
|  | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     TextField { | ||||
|         id: userPass | ||||
|         anchors.top: description.bottom | ||||
|         placeholderText: qsTr(placeholder) | ||||
|         echoMode: TextInput.Password | ||||
|         onTextChanged: validateFunc(userPass, userPassRepeat, errorText) | ||||
|         text: config.userPassword | ||||
|  | ||||
|         /* Let the virtual keyboard change to digits only */ | ||||
|         inputMethodHints: hints | ||||
|         onActiveFocusChanged: { | ||||
|             if(activeFocus) { | ||||
|                 Qt.inputMethod.update(Qt.ImQueryInput) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     TextField { | ||||
|         id: userPassRepeat | ||||
|         anchors.top: userPass.bottom | ||||
|         placeholderText: qsTr(placeholder + " (repeat)") | ||||
|         inputMethodHints: hints | ||||
|         echoMode: TextInput.Password | ||||
|         onTextChanged: validateFunc(userPass, userPassRepeat, errorText) | ||||
|         text: config.userPassword | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Text { | ||||
|         anchors.top: userPassRepeat.bottom | ||||
|         id: errorText | ||||
|         visible: false | ||||
|         wrapMode: Text.WordWrap | ||||
|  | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.topMargin: 50 | ||||
|         width: 500 | ||||
|     } | ||||
|  | ||||
|     Button { | ||||
|         anchors.horizontalCenter: parent.horizontalCenter | ||||
|         anchors.top: errorText.bottom | ||||
|         anchors.topMargin: 40 | ||||
|         width: 500 | ||||
|  | ||||
|         text: qsTr("Continue") | ||||
|         onClicked: { | ||||
|             if (validateFunc(userPass, userPassRepeat, errorText)) { | ||||
|                 config.userPassword = userPass.text; | ||||
|                 navNext(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Page | ||||
| { | ||||
|     id: fdeWait | ||||
|  | ||||
|     Item { | ||||
|         anchors.left: parent.left | ||||
|         anchors.top: parent.top | ||||
|         anchors.right: parent.right | ||||
|         width: parent.width | ||||
|         height: parent.height | ||||
|  | ||||
|         Image { | ||||
|             id: logo | ||||
|             anchors.horizontalCenter: parent.horizontalCenter | ||||
|             anchors.top: parent.top | ||||
|             anchors.topMargin: 50 | ||||
|             height: 250 | ||||
|             fillMode: Image.PreserveAspectFit | ||||
|             source: "file:///usr/share/calamares/branding/default-mobile/logo.png" | ||||
|         } | ||||
|         Text { | ||||
|             id: waitText | ||||
|             anchors.horizontalCenter: parent.horizontalCenter | ||||
|             anchors.top: logo.bottom | ||||
|             anchors.topMargin: 150 | ||||
|             wrapMode: Text.WordWrap | ||||
|             text: "Formatting and mounting target partition. This may" + | ||||
|                   " take up to two minutes, please be patient." | ||||
|             width: 500 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,65 +0,0 @@ | ||||
| /* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org> | ||||
|  * SPDX-License-Identifier: GPL-3.0-or-later */ | ||||
| import io.calamares.core 1.0 | ||||
| import io.calamares.ui 1.0 | ||||
|  | ||||
| import QtQuick 2.10 | ||||
| import QtQuick.Controls 2.10 | ||||
| import QtQuick.Layouts 1.3 | ||||
| import org.kde.kirigami 2.7 as Kirigami | ||||
| import QtGraphicalEffects 1.0 | ||||
| import QtQuick.Window 2.3 | ||||
| import QtQuick.VirtualKeyboard 2.1 | ||||
|  | ||||
| Page | ||||
| { | ||||
|     id: welcome | ||||
|  | ||||
|     Item { | ||||
|         id: appContainer | ||||
|         anchors.left: parent.left | ||||
|         anchors.top: parent.top | ||||
|         anchors.right: parent.right | ||||
|         Item { | ||||
|             width: parent.width | ||||
|             height: parent.height | ||||
|  | ||||
|             Image { | ||||
|                 id: logo | ||||
|                 anchors.horizontalCenter: parent.horizontalCenter | ||||
|                 anchors.top: parent.top | ||||
|                 anchors.topMargin: 50 | ||||
|                 height: 250 | ||||
|                 fillMode: Image.PreserveAspectFit | ||||
|                 source: "file:///usr/share/calamares/branding/default-mobile/logo.png" | ||||
|             } | ||||
|             Text { | ||||
|                 id: mainText | ||||
|                 anchors.horizontalCenter: parent.horizontalCenter | ||||
|                 anchors.top: logo.bottom | ||||
|                 anchors.topMargin: 50 | ||||
|                 horizontalAlignment: Text.AlignRight | ||||
|                 text: "You are about to install<br>" + | ||||
|                       "<b>" + config.osName + | ||||
|                       " " + config.version + "</b><br>" + | ||||
|                       "user interface " + | ||||
|                       "<b>" + config.userInterface + "</b><br>" + | ||||
|                       "architecture " + | ||||
|                       "<b>" + config.arch + "</b><br>" + | ||||
|                       "on your " + | ||||
|                       "<b>" + config.device + "</b><br>" | ||||
|                 width: 500 | ||||
|             } | ||||
|  | ||||
|             Button { | ||||
|                 anchors.horizontalCenter: parent.horizontalCenter | ||||
|                 anchors.top: mainText.bottom | ||||
|                 anchors.topMargin: 50 | ||||
|                 width: 500 | ||||
|  | ||||
|                 text: qsTr("Continue") | ||||
|                 onClicked: navNext() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,20 +0,0 @@ | ||||
| # The OS-FreeBSD module does "all the things" in a FreeBSD installation. | ||||
| # Since the other modules -- users, fstab, grub, pretty much all of them | ||||
| # -- are Linux-specific, it doesn't make much sense to fork each of them | ||||
| # or provide alternatives, so instead we have one module that completes | ||||
| # a FreeBSD installation based on the GlobalStorage values set by | ||||
| # Calamares viewmodules. | ||||
| # | ||||
| #   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
| #   SPDX-License-Identifier: GPL-3.0-or-later | ||||
| #   License-Filename: LICENSE | ||||
| # | ||||
|  | ||||
| calamares_add_plugin( os-freebsd | ||||
|     TYPE job | ||||
|     EXPORT_MACRO PLUGINDLLEXPORT_PRO | ||||
|     SOURCES | ||||
|         FreeBSDJob.cpp | ||||
|     SHARED_LIB | ||||
|     NO_CONFIG | ||||
| ) | ||||
| @@ -1,58 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  *   License-Filename: LICENSE | ||||
|  */ | ||||
|  | ||||
| #include "FreeBSDJob.h" | ||||
|  | ||||
| #include "CalamaresVersion.h" | ||||
| #include "GlobalStorage.h" | ||||
| #include "JobQueue.h" | ||||
| #include "utils/Logger.h" | ||||
|  | ||||
| #include <QDateTime> | ||||
| #include <QProcess> | ||||
| #include <QThread> | ||||
|  | ||||
|  | ||||
| FreeBSDJob::FreeBSDJob( QObject* parent ) | ||||
|     : Calamares::CppJob( parent ) | ||||
| { | ||||
| } | ||||
|  | ||||
|  | ||||
| FreeBSDJob::~FreeBSDJob() {} | ||||
|  | ||||
|  | ||||
| QString | ||||
| FreeBSDJob::prettyName() const | ||||
| { | ||||
|     return tr( "FreeBSD Installation Job" ); | ||||
| } | ||||
|  | ||||
| Calamares::JobResult | ||||
| FreeBSDJob::exec() | ||||
| { | ||||
|     emit progress( 0.1 ); | ||||
|     cDebug() << "[FREEBSD]"; | ||||
|  | ||||
|     Calamares::JobQueue::instance()->globalStorage()->debugDump(); | ||||
|     emit progress( 0.5 ); | ||||
|  | ||||
|     QThread::sleep( 3 ); | ||||
|     emit progress( 1.0 ); | ||||
|  | ||||
|     return Calamares::JobResult::ok(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void | ||||
| FreeBSDJob::setConfigurationMap( const QVariantMap& configurationMap ) | ||||
| { | ||||
|     // TODO: actually fetch something from that configuration | ||||
|     m_configurationMap = configurationMap; | ||||
| } | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDJobFactory, registerPlugin< FreeBSDJob >(); ) | ||||
| @@ -1,39 +0,0 @@ | ||||
| /* === This file is part of Calamares - <https://github.com/calamares> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  *   License-Filename: LICENSE | ||||
|  */ | ||||
|  | ||||
| #ifndef FREEBSDJOB_H | ||||
| #define FREEBSDJOB_H | ||||
|  | ||||
| #include "CppJob.h" | ||||
| #include "DllMacro.h" | ||||
| #include "utils/PluginFactory.h" | ||||
|  | ||||
| #include <QObject> | ||||
| #include <QVariantMap> | ||||
|  | ||||
|  | ||||
| class PLUGINDLLEXPORT FreeBSDJob : public Calamares::CppJob | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit FreeBSDJob( QObject* parent = nullptr ); | ||||
|     virtual ~FreeBSDJob() override; | ||||
|  | ||||
|     QString prettyName() const override; | ||||
|  | ||||
|     Calamares::JobResult exec() override; | ||||
|  | ||||
|     void setConfigurationMap( const QVariantMap& configurationMap ) override; | ||||
|  | ||||
| private: | ||||
|     QVariantMap m_configurationMap; | ||||
| }; | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDJobFactory ) | ||||
|  | ||||
| #endif  // FREEBSDJOB_H | ||||
| @@ -1,83 +0,0 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| # === This file is part of Calamares - <https://github.com/calamares> === | ||||
| # | ||||
| #   Calamares is free software: you can redistribute it and/or modify | ||||
| #   it under the terms of the GNU General Public License as published by | ||||
| #   the Free Software Foundation, either version 3 of the License, or | ||||
| #   (at your option) any later version. | ||||
| # | ||||
| #   Calamares is distributed in the hope that it will be useful, | ||||
| #   but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| #   GNU General Public License for more details. | ||||
| # | ||||
| #   You should have received a copy of the GNU General Public License | ||||
| #   along with Calamares. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| #   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
| #   SPDX-License-Identifier: GPL-3.0-or-later | ||||
| #   License-Filename: LICENSE | ||||
| # | ||||
|  | ||||
| """ | ||||
| === NixOS Configuration | ||||
|  | ||||
| NixOS has its own "do all the things" configuration file which | ||||
| declaratively handles what things need to be done in the target | ||||
| system, and it has an existing tool to "execute" that declarative | ||||
| specification. This module takes configuration values set by | ||||
| Calamares viewmodules (e.g. the users module) and puts | ||||
| them into the configuration file in the target system, | ||||
| and then runs the necessary NixOS specific tools. | ||||
| """ | ||||
|  | ||||
| import libcalamares | ||||
| import os | ||||
| from time import gmtime, strftime, sleep | ||||
|  | ||||
| import gettext | ||||
| _ = gettext.translation("calamares-python", | ||||
|                         localedir=libcalamares.utils.gettext_path(), | ||||
|                         languages=libcalamares.utils.gettext_languages(), | ||||
|                         fallback=True).gettext | ||||
|  | ||||
|  | ||||
| def pretty_name(): | ||||
|     return _("NixOS Configuration.") | ||||
|  | ||||
|  | ||||
| def run(): | ||||
|     """NixOS Configuration.""" | ||||
|     libcalamares.utils.debug("LocaleDir=" + | ||||
|                              str(libcalamares.utils.gettext_path())) | ||||
|     libcalamares.utils.debug("Languages=" + | ||||
|                              str(libcalamares.utils.gettext_languages())) | ||||
|  | ||||
|     # TODO: probably want to use the job configuration | ||||
|     #       with a key "stage" to distinguish generate-config | ||||
|     #       from execute-config; maybe it wants an "all" as well | ||||
|     #       to do both. | ||||
|     accumulator = "*** Job configuration\n" | ||||
|     accumulator += str(libcalamares.job.configuration) | ||||
|     libcalamares.utils.debug(accumulator) | ||||
|  | ||||
|     accumulator = "*** GlobalStorage configuration\n" | ||||
|     accumulator += "count: " + str(libcalamares.globalstorage.count()) + "\n" | ||||
|     accumulator += "keys: {}\n".format(str(libcalamares.globalstorage.keys())) | ||||
|     libcalamares.utils.debug(accumulator) | ||||
|  | ||||
|     libcalamares.utils.debug("Run NixOS tools.") | ||||
|  | ||||
|     libcalamares.job.setprogress( 0.1 ) | ||||
|     sleep(1) | ||||
|     libcalamares.job.setprogress( 0.5 ) | ||||
|     sleep(1) | ||||
|     libcalamares.job.setprogress( 1.0 ) | ||||
|  | ||||
|     sleep(3) | ||||
|  | ||||
|     # To indicate an error, return a tuple of: | ||||
|     # (message, detailed-error-message) | ||||
|     return None | ||||
							
								
								
									
										68
									
								
								modules/packagechooser/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,68 @@ | ||||
| # === This file is part of Calamares - <https://calamares.io> === | ||||
| # | ||||
| #   SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org> | ||||
| #   SPDX-License-Identifier: BSD-2-Clause | ||||
| # | ||||
| find_package(${qtname} COMPONENTS Core Gui Widgets REQUIRED) | ||||
| set(_extra_libraries "") | ||||
| set(_extra_src "") | ||||
|  | ||||
| ### OPTIONAL AppData XML support in PackageModel | ||||
| # | ||||
| # | ||||
| option(BUILD_APPDATA "Support appdata: items in PackageChooser (requires QtXml)" ON) | ||||
| if(BUILD_APPDATA) | ||||
|     find_package(${qtname} COMPONENTS Xml) | ||||
|     if(TARGET ${qtname}::Xml) | ||||
|         add_definitions(-DHAVE_APPDATA) | ||||
|         list(APPEND _extra_libraries ${qtname}::Xml) | ||||
|         list(APPEND _extra_src ItemAppData.cpp) | ||||
|     endif() | ||||
| endif() | ||||
|  | ||||
| ### OPTIONAL AppStream support in PackageModel | ||||
| # | ||||
| # | ||||
| option(BUILD_APPSTREAM "Support appstream: items in PackageChooser (requires libappstream-qt)" ON) | ||||
| if(BUILD_APPSTREAM) | ||||
|     find_package(AppStreamQt) | ||||
|     set_package_properties( | ||||
|         AppStreamQt | ||||
|         PROPERTIES | ||||
|         DESCRIPTION "Support for AppStream (cache) data" | ||||
|         URL "https://github.com/ximion/appstream" | ||||
|         PURPOSE "AppStream provides package data" | ||||
|         TYPE OPTIONAL | ||||
|     ) | ||||
|     if(AppStreamQt_FOUND) | ||||
|         add_definitions(-DHAVE_APPSTREAM_VERSION=${AppStreamQt_VERSION_MAJOR}) | ||||
|         list(APPEND _extra_libraries AppStreamQt) | ||||
|         list(APPEND _extra_src ItemAppStream.cpp) | ||||
|     endif() | ||||
| endif() | ||||
|  | ||||
| calamares_add_plugin(packagechooser | ||||
|     TYPE viewmodule | ||||
|     EXPORT_MACRO PLUGINDLLEXPORT_PRO | ||||
|     SOURCES | ||||
|         Config.cpp | ||||
|         PackageChooserPage.cpp | ||||
|         PackageChooserViewStep.cpp | ||||
|         PackageModel.cpp | ||||
|         ${_extra_src} | ||||
|     RESOURCES | ||||
|         packagechooser.qrc | ||||
|     UI | ||||
|         page_package.ui | ||||
|     LINK_PRIVATE_LIBRARIES | ||||
|         ${_extra_libraries} | ||||
|     SHARED_LIB | ||||
| ) | ||||
|  | ||||
| # include(CalamaresAddTest) | ||||
| # calamares_add_test( | ||||
| #     packagechoosertest | ||||
| #     GUI | ||||
| #     SOURCES Tests.cpp | ||||
| #     LIBRARIES calamares_viewmodule_packagechooser ${_extra_libraries} | ||||
| # ) | ||||
							
								
								
									
										361
									
								
								modules/packagechooser/Config.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,361 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-FileCopyrightText: 2021 Anke Boersma <demm@kaosx.us> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "Config.h" | ||||
|  | ||||
| #ifdef HAVE_APPDATA | ||||
| #include "ItemAppData.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_APPSTREAM_VERSION | ||||
| #include "ItemAppStream.h" | ||||
| #include <AppStreamQt/pool.h> | ||||
| #include <memory> | ||||
| #endif | ||||
|  | ||||
| #include "GlobalStorage.h" | ||||
| #include "JobQueue.h" | ||||
| #include "compat/Variant.h" | ||||
| #include "packages/Globals.h" | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| /** @brief This removes any values from @p groups that match @p source | ||||
|  * | ||||
|  * This is used to remove duplicates from the netinstallAdd structure | ||||
|  * It iterates over @p groups and for each map in the list, if the | ||||
|  * "source" element matches @p source, it is removed from the returned | ||||
|  * list. | ||||
|  */ | ||||
| static QVariantList | ||||
| pruneNetinstallAdd( const QString& source, const QVariant& groups ) | ||||
| { | ||||
|     QVariantList newGroupList; | ||||
|     const QVariantList groupList = groups.toList(); | ||||
|     for ( const QVariant& group : groupList ) | ||||
|     { | ||||
|         QVariantMap groupMap = group.toMap(); | ||||
|         if ( groupMap.value( "source", "" ).toString() != source ) | ||||
|         { | ||||
|             newGroupList.append( groupMap ); | ||||
|         } | ||||
|     } | ||||
|     return newGroupList; | ||||
| } | ||||
|  | ||||
| const NamedEnumTable< PackageChooserMode >& | ||||
| packageChooserModeNames() | ||||
| { | ||||
|     static const NamedEnumTable< PackageChooserMode > names { | ||||
|         { "optional", PackageChooserMode::Optional }, | ||||
|         { "required", PackageChooserMode::Required }, | ||||
|         { "optionalmultiple", PackageChooserMode::OptionalMultiple }, | ||||
|         { "requiredmultiple", PackageChooserMode::RequiredMultiple }, | ||||
|         // and a bunch of aliases | ||||
|         { "zero-or-one", PackageChooserMode::Optional }, | ||||
|         { "radio", PackageChooserMode::Required }, | ||||
|         { "one", PackageChooserMode::Required }, | ||||
|         { "set", PackageChooserMode::OptionalMultiple }, | ||||
|         { "zero-or-more", PackageChooserMode::OptionalMultiple }, | ||||
|         { "multiple", PackageChooserMode::RequiredMultiple }, | ||||
|         { "one-or-more", PackageChooserMode::RequiredMultiple } | ||||
|     }; | ||||
|     return names; | ||||
| } | ||||
|  | ||||
| const NamedEnumTable< PackageChooserMethod >& | ||||
| PackageChooserMethodNames() | ||||
| { | ||||
|     static const NamedEnumTable< PackageChooserMethod > names { | ||||
|         { "legacy", PackageChooserMethod::Legacy }, | ||||
|         { "custom", PackageChooserMethod::Legacy }, | ||||
|         { "contextualprocess", PackageChooserMethod::Legacy }, | ||||
|         { "packages", PackageChooserMethod::Packages }, | ||||
|         { "netinstall-add", PackageChooserMethod::NetAdd }, | ||||
|         { "netinstall-select", PackageChooserMethod::NetSelect }, | ||||
|     }; | ||||
|     return names; | ||||
| } | ||||
|  | ||||
| Config::Config( QObject* parent ) | ||||
|     : Calamares::ModuleSystem::Config( parent ) | ||||
|     , m_model( new PackageListModel( this ) ) | ||||
|     , m_mode( PackageChooserMode::Required ) | ||||
| { | ||||
| } | ||||
|  | ||||
| Config::~Config() {} | ||||
|  | ||||
| const PackageItem& | ||||
| Config::introductionPackage() const | ||||
| { | ||||
|     for ( int i = 0; i < m_model->packageCount(); ++i ) | ||||
|     { | ||||
|         const auto& package = m_model->packageData( i ); | ||||
|         if ( package.isNonePackage() ) | ||||
|         { | ||||
|             return package; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static PackageItem* defaultIntroduction = nullptr; | ||||
|     if ( !defaultIntroduction ) | ||||
|     { | ||||
|         const auto name = QT_TR_NOOP( "Package Selection" ); | ||||
|         const auto description | ||||
|             = QT_TR_NOOP( "Please pick a product from the list. The selected product will be installed." ); | ||||
|         defaultIntroduction = new PackageItem( QString(), name, description ); | ||||
|         defaultIntroduction->screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); | ||||
|         defaultIntroduction->name = Calamares::Locale::TranslatedString( name, metaObject()->className() ); | ||||
|         defaultIntroduction->description | ||||
|             = Calamares::Locale::TranslatedString( description, metaObject()->className() ); | ||||
|     } | ||||
|     return *defaultIntroduction; | ||||
| } | ||||
|  | ||||
| static inline QString | ||||
| make_gs_key( const Calamares::ModuleSystem::InstanceKey& key ) | ||||
| { | ||||
|     return QStringLiteral( "packagechooser_" ) + key.id(); | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::updateGlobalStorage( const QStringList& selected ) const | ||||
| { | ||||
|     if ( m_packageChoice.has_value() ) | ||||
|     { | ||||
|         cWarning() << "Inconsistent package choices -- both model and single-selection QML"; | ||||
|     } | ||||
|     if ( m_method == PackageChooserMethod::Legacy ) | ||||
|     { | ||||
|         QString value = selected.join( ',' ); | ||||
|         Calamares::JobQueue::instance()->globalStorage()->insert( make_gs_key( m_defaultId ), value ); | ||||
|         cDebug() << m_defaultId << "selected" << value; | ||||
|     } | ||||
|     else if ( m_method == PackageChooserMethod::Packages ) | ||||
|     { | ||||
|         QStringList packageNames = m_model->getInstallPackagesForNames( selected ); | ||||
|         cDebug() << m_defaultId << "packages to install" << packageNames; | ||||
|         Calamares::Packages::setGSPackageAdditions( | ||||
|             Calamares::JobQueue::instance()->globalStorage(), m_defaultId, packageNames ); | ||||
|     } | ||||
|     else if ( m_method == PackageChooserMethod::NetAdd ) | ||||
|     { | ||||
|         QVariantList netinstallDataList = m_model->getNetinstallDataForNames( selected ); | ||||
|         if ( netinstallDataList.isEmpty() ) | ||||
|         { | ||||
|             cWarning() << "No netinstall information found for " << selected; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // If an earlier packagechooser instance added this data to global storage, combine them | ||||
|             auto* gs = Calamares::JobQueue::instance()->globalStorage(); | ||||
|             if ( gs->contains( "netinstallAdd" ) ) | ||||
|             { | ||||
|                 netinstallDataList | ||||
|                     += pruneNetinstallAdd( QStringLiteral( "packageChooser" ), gs->value( "netinstallAdd" ) ); | ||||
|             } | ||||
|             gs->insert( "netinstallAdd", netinstallDataList ); | ||||
|         } | ||||
|     } | ||||
|     else if ( m_method == PackageChooserMethod::NetSelect ) | ||||
|     { | ||||
|         cDebug() << m_defaultId << "groups to select in netinstall" << selected; | ||||
|         QStringList newSelected = selected; | ||||
|         auto* gs = Calamares::JobQueue::instance()->globalStorage(); | ||||
|  | ||||
|         // If an earlier packagechooser instance added this data to global storage, combine them | ||||
|         if ( gs->contains( "netinstallSelect" ) ) | ||||
|         { | ||||
|             auto selectedOrig = gs->value( "netinstallSelect" ); | ||||
|             if ( selectedOrig.canConvert< QStringList >() ) | ||||
|             { | ||||
|                 newSelected += selectedOrig.toStringList(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 cWarning() << "Invalid NetinstallSelect data in global storage.  Earlier selections purged"; | ||||
|             } | ||||
|             gs->remove( "netinstallSelect" ); | ||||
|         } | ||||
|         gs->insert( "netinstallSelect", newSelected ); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         cWarning() << "Unknown packagechooser method" << smash( m_method ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::updateGlobalStorage() const | ||||
| { | ||||
|     if ( m_model->packageCount() > 0 ) | ||||
|     { | ||||
|         cWarning() << "Inconsistent package choices -- both model and single-selection QML"; | ||||
|     } | ||||
|     if ( m_method == PackageChooserMethod::Legacy ) | ||||
|     { | ||||
|         auto* gs = Calamares::JobQueue::instance()->globalStorage(); | ||||
|         if ( m_packageChoice.has_value() ) | ||||
|         { | ||||
|             gs->insert( make_gs_key( m_defaultId ), m_packageChoice.value() ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             gs->remove( make_gs_key( m_defaultId ) ); | ||||
|         } | ||||
|     } | ||||
|     else if ( m_method == PackageChooserMethod::Packages ) | ||||
|     { | ||||
|         cWarning() << "Unsupported single-selection packagechooser method 'Packages'"; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         cWarning() << "Unknown packagechooser method" << smash( m_method ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setPackageChoice( const QString& packageChoice ) | ||||
| { | ||||
|     if ( packageChoice.isEmpty() ) | ||||
|     { | ||||
|         m_packageChoice.reset(); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         m_packageChoice = packageChoice; | ||||
|     } | ||||
|     emit packageChoiceChanged( m_packageChoice.value_or( QString() ) ); | ||||
| } | ||||
|  | ||||
| QString | ||||
| Config::prettyName() const | ||||
| { | ||||
|     return m_stepName ? m_stepName->get() : tr( "Packages" ); | ||||
| } | ||||
|  | ||||
| QString | ||||
| Config::prettyStatus() const | ||||
| { | ||||
|     return tr( "Install option: <strong>%1</strong>" ).arg( m_packageChoice.value_or( tr( "None" ) ) ); | ||||
| } | ||||
|  | ||||
| static void | ||||
| fillModel( PackageListModel* model, const QVariantList& items ) | ||||
| { | ||||
|     if ( items.isEmpty() ) | ||||
|     { | ||||
|         cWarning() << "No *items* for PackageChooser module."; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| #ifdef HAVE_APPSTREAM_VERSION | ||||
|     std::unique_ptr< AppStream::Pool > pool; | ||||
|     bool poolOk = false; | ||||
| #endif | ||||
|  | ||||
|     cDebug() << "Loading PackageChooser model items from config"; | ||||
|     int item_index = 0; | ||||
|     for ( const auto& item_it : items ) | ||||
|     { | ||||
|         ++item_index; | ||||
|         QVariantMap item_map = item_it.toMap(); | ||||
|         if ( item_map.isEmpty() ) | ||||
|         { | ||||
|             cWarning() << "PackageChooser entry" << item_index << "is not valid."; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if ( item_map.contains( "appdata" ) ) | ||||
|         { | ||||
| #ifdef HAVE_XML | ||||
|             model->addPackage( fromAppData( item_map ) ); | ||||
| #else | ||||
|             cWarning() << "Loading AppData XML is not supported."; | ||||
| #endif | ||||
|         } | ||||
|         else if ( item_map.contains( "appstream" ) ) | ||||
|         { | ||||
| #ifdef HAVE_APPSTREAM_VERSION | ||||
|             if ( !pool ) | ||||
|             { | ||||
|                 pool = std::make_unique< AppStream::Pool >(); | ||||
|                 pool->setLocale( QStringLiteral( "ALL" ) ); | ||||
|                 poolOk = pool->load(); | ||||
|             } | ||||
|             if ( pool && poolOk ) | ||||
|             { | ||||
|                 model->addPackage( fromAppStream( *pool, item_map ) ); | ||||
|             } | ||||
| #else | ||||
|             cWarning() << "Loading AppStream data is not supported."; | ||||
| #endif | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             model->addPackage( PackageItem( item_map ) ); | ||||
|         } | ||||
|     } | ||||
|     cDebug() << Logger::SubEntry << "Loaded PackageChooser with" << model->packageCount() << "entries."; | ||||
| } | ||||
|  | ||||
| void | ||||
| Config::setConfigurationMap( const QVariantMap& configurationMap ) | ||||
| { | ||||
|     m_mode = packageChooserModeNames().find( Calamares::getString( configurationMap, "mode" ), | ||||
|                                              PackageChooserMode::Required ); | ||||
|     m_method = PackageChooserMethodNames().find( Calamares::getString( configurationMap, "method" ), | ||||
|                                                  PackageChooserMethod::Legacy ); | ||||
|  | ||||
|     if ( m_method == PackageChooserMethod::Legacy ) | ||||
|     { | ||||
|         cDebug() << "Using module ID" << m_defaultId; | ||||
|     } | ||||
|  | ||||
|     if ( configurationMap.contains( "items" ) ) | ||||
|     { | ||||
|         fillModel( m_model, configurationMap.value( "items" ).toList() ); | ||||
|  | ||||
|         QString default_item_id = Calamares::getString( configurationMap, "default" ); | ||||
|         if ( !default_item_id.isEmpty() ) | ||||
|         { | ||||
|             for ( int item_n = 0; item_n < m_model->packageCount(); ++item_n ) | ||||
|             { | ||||
|                 QModelIndex item_idx = m_model->index( item_n, 0 ); | ||||
|                 QVariant item_id = m_model->data( item_idx, PackageListModel::IdRole ); | ||||
|  | ||||
|                 if ( item_id.toString() == default_item_id ) | ||||
|                 { | ||||
|                     m_defaultModelIndex = item_idx; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         setPackageChoice( Calamares::getString( configurationMap, "packageChoice" ) ); | ||||
|         if ( m_method != PackageChooserMethod::Legacy ) | ||||
|         { | ||||
|             cWarning() << "Single-selection QML module must use 'Legacy' method."; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     bool labels_ok = false; | ||||
|     auto labels = Calamares::getSubMap( configurationMap, "labels", labels_ok ); | ||||
|     if ( labels_ok ) | ||||
|     { | ||||
|         if ( labels.contains( "step" ) ) | ||||
|         { | ||||
|             m_stepName = new Calamares::Locale::TranslatedString( labels, "step" ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										128
									
								
								modules/packagechooser/Config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,128 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2021 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-FileCopyrightText: 2021 Anke Boersma <demm@kaosx.us> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef PACKAGECHOOSER_CONFIG_H | ||||
| #define PACKAGECHOOSER_CONFIG_H | ||||
|  | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| #include "modulesystem/Config.h" | ||||
| #include "modulesystem/InstanceKey.h" | ||||
|  | ||||
| #include <memory> | ||||
| #include <optional> | ||||
|  | ||||
| enum class PackageChooserMode | ||||
| { | ||||
|     Optional,  // zero or one | ||||
|     Required,  // exactly one | ||||
|     OptionalMultiple,  // zero or more | ||||
|     RequiredMultiple  // one or more | ||||
| }; | ||||
|  | ||||
| const NamedEnumTable< PackageChooserMode >& packageChooserModeNames(); | ||||
|  | ||||
| enum class PackageChooserMethod | ||||
| { | ||||
|     Legacy,  // use contextualprocess or other custom | ||||
|     Packages,  // use the packages module | ||||
|     NetAdd,  // adds packages to the netinstall module | ||||
|     NetSelect,  // makes selections in the netinstall module | ||||
| }; | ||||
|  | ||||
| const NamedEnumTable< PackageChooserMethod >& PackageChooserMethodNames(); | ||||
|  | ||||
| class Config : public Calamares::ModuleSystem::Config | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
|     /** @brief This is the single-select package-choice | ||||
|      * | ||||
|      * For (QML) modules that support only a single selection and | ||||
|      * just want to do things in a straightforward pick-this-one | ||||
|      * way, the packageChoice property is a (the) way to go. | ||||
|      * | ||||
|      * Writing to this property means that any other form of package- | ||||
|      * choice or selection is ignored. | ||||
|      */ | ||||
|     Q_PROPERTY( QString packageChoice READ packageChoice WRITE setPackageChoice NOTIFY packageChoiceChanged ) | ||||
|     Q_PROPERTY( QString prettyStatus READ prettyStatus NOTIFY prettyStatusChanged FINAL ) | ||||
|  | ||||
| public: | ||||
|     Config( QObject* parent = nullptr ); | ||||
|     ~Config() override; | ||||
|  | ||||
|     /** @brief Sets the default Id for this Config | ||||
|      * | ||||
|      * The default Id is the (owning) module identifier for the config, | ||||
|      * and it is used when the Id read from the config file is empty. | ||||
|      * The **usual** configuration when using method *packages* is | ||||
|      * to rely on the default Id. | ||||
|      */ | ||||
|     void setDefaultId( const Calamares::ModuleSystem::InstanceKey& defaultId ) { m_defaultId = defaultId; } | ||||
|     void setConfigurationMap( const QVariantMap& ) override; | ||||
|  | ||||
|     PackageChooserMode mode() const { return m_mode; } | ||||
|     PackageListModel* model() const { return m_model; } | ||||
|     QModelIndex defaultSelectionIndex() const { return m_defaultModelIndex; } | ||||
|  | ||||
|     /** @brief Returns an "introductory package" which describes packagechooser | ||||
|      * | ||||
|      * If the model contains a "none" package, returns that one on | ||||
|      * the assumption that it is one to describe the whole; otherwise | ||||
|      * returns a totally generic description. | ||||
|      */ | ||||
|     const PackageItem& introductionPackage() const; | ||||
|  | ||||
|     /** @brief Write selection to global storage | ||||
|      * | ||||
|      * Updates the GS keys for this packagechooser, marking all | ||||
|      * (and only) the packages in @p selected as selected. | ||||
|      */ | ||||
|     void updateGlobalStorage( const QStringList& selected ) const; | ||||
|     /** @brief Write selection to global storage | ||||
|      * | ||||
|      * Updates the GS keys for this packagechooser, marking **only** | ||||
|      * the package choice as selected. This assumes that the single- | ||||
|      * selection QML code is in use. | ||||
|      */ | ||||
|     void updateGlobalStorage() const; | ||||
|  | ||||
|     QString packageChoice() const { return m_packageChoice.value_or( QString() ); } | ||||
|     void setPackageChoice( const QString& packageChoice ); | ||||
|  | ||||
|     QString prettyName() const; | ||||
|     QString prettyStatus() const; | ||||
|  | ||||
| signals: | ||||
|     void packageChoiceChanged( QString packageChoice ); | ||||
|     void prettyStatusChanged(); | ||||
|  | ||||
| private: | ||||
|     PackageListModel* m_model = nullptr; | ||||
|     QModelIndex m_defaultModelIndex; | ||||
|  | ||||
|     /// Selection mode for this module | ||||
|     PackageChooserMode m_mode = PackageChooserMode::Optional; | ||||
|     /// How this module stores to GS | ||||
|     PackageChooserMethod m_method = PackageChooserMethod::Legacy; | ||||
|     /// Value to use for id if none is set in the config file | ||||
|     Calamares::ModuleSystem::InstanceKey m_defaultId; | ||||
|     /** @brief QML selection (for single-selection approaches) | ||||
|      * | ||||
|      * If there is no value, then there has been no selection. | ||||
|      * Reading the property will return an empty QString. | ||||
|      */ | ||||
|     std::optional< QString > m_packageChoice; | ||||
|     Calamares::Locale::TranslatedString* m_stepName;  // As it appears in the sidebar | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										225
									
								
								modules/packagechooser/ItemAppData.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,225 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /** @brief Loading items from AppData XML files. | ||||
|  * | ||||
|  * Only used if QtXML is found, implements PackageItem::fromAppData(). | ||||
|  */ | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| #include <QDomDocument> | ||||
| #include <QDomNodeList> | ||||
| #include <QFile> | ||||
|  | ||||
| /** @brief try to load the given @p fileName XML document | ||||
|  * | ||||
|  * Returns a QDomDocument, which will be valid iff the file can | ||||
|  * be read and contains valid XML data. | ||||
|  */ | ||||
| static inline QDomDocument | ||||
| loadAppData( const QString& fileName ) | ||||
| { | ||||
|     QFile file( fileName ); | ||||
|     if ( !file.open( QIODevice::ReadOnly ) ) | ||||
|     { | ||||
|         return QDomDocument(); | ||||
|     } | ||||
|     QDomDocument doc( "AppData" ); | ||||
|     if ( !doc.setContent( &file ) ) | ||||
|     { | ||||
|         file.close(); | ||||
|         return QDomDocument(); | ||||
|     } | ||||
|     file.close(); | ||||
|     return doc; | ||||
| } | ||||
|  | ||||
| /** @brief gets the text of child element @p tagName | ||||
|  */ | ||||
| static inline QString | ||||
| getChildText( const QDomNode& n, const QString& tagName ) | ||||
| { | ||||
|     QDomElement e = n.firstChildElement( tagName ); | ||||
|     return e.isNull() ? QString() : e.text(); | ||||
| } | ||||
|  | ||||
| /** @brief Gets a suitable screenshot path | ||||
|  * | ||||
|  * The <screenshots> element contains zero or more <screenshot> | ||||
|  * elements, which can have a *type* associated with them. | ||||
|  * Scan the screenshot elements, return the <image> path | ||||
|  * for the one labeled with type=default or, if there is no | ||||
|  * default, the first element. | ||||
|  */ | ||||
| static inline QString | ||||
| getScreenshotPath( const QDomNode& n ) | ||||
| { | ||||
|     QDomElement shotsNode = n.firstChildElement( "screenshots" ); | ||||
|     if ( shotsNode.isNull() ) | ||||
|     { | ||||
|         return QString(); | ||||
|     } | ||||
|  | ||||
|     const QDomNodeList shotList = shotsNode.childNodes(); | ||||
|     int firstScreenshot = -1;  // Use which screenshot node? | ||||
|     for ( int i = 0; i < shotList.count(); ++i ) | ||||
|     { | ||||
|         if ( !shotList.at( i ).isElement() ) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|         QDomElement e = shotList.at( i ).toElement(); | ||||
|         if ( e.tagName() != "screenshot" ) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|         // If none has the "type=default" attribute, use the first one | ||||
|         if ( firstScreenshot < 0 ) | ||||
|         { | ||||
|             firstScreenshot = i; | ||||
|         } | ||||
|         // But type=default takes precedence. | ||||
|         if ( e.hasAttribute( "type" ) && e.attribute( "type" ) == "default" ) | ||||
|         { | ||||
|             firstScreenshot = i; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if ( firstScreenshot >= 0 ) | ||||
|     { | ||||
|         return shotList.at( firstScreenshot ).firstChildElement( "image" ).text(); | ||||
|     } | ||||
|  | ||||
|     return QString(); | ||||
| } | ||||
|  | ||||
| /** @brief Returns language of the given element @p e | ||||
|  * | ||||
|  * Transforms the attribute value for xml:lang to something | ||||
|  * suitable for TranslatedString (e.g. [lang]). | ||||
|  */ | ||||
| static inline QString | ||||
| getLanguage( const QDomElement& e ) | ||||
| { | ||||
|     QString language = e.attribute( "xml:lang" ); | ||||
|     if ( !language.isEmpty() ) | ||||
|     { | ||||
|         language.replace( '-', '_' ); | ||||
|         language.prepend( '[' ); | ||||
|         language.append( ']' ); | ||||
|     } | ||||
|     return language; | ||||
| } | ||||
|  | ||||
| /** @brief Scan the list of @p children for @p tagname elements and add them to the map | ||||
|  * | ||||
|  * Uses @p mapname instead of @p tagname for the entries in map @p m | ||||
|  * to allow renaming from XML to map keys (in particular for | ||||
|  * TranslatedString). Also transforms xml:lang attributes to suitable | ||||
|  * key-decorations on @p mapname. | ||||
|  */ | ||||
| static inline void | ||||
| fillMap( QVariantMap& m, const QDomNodeList& children, const QString& tagname, const QString& mapname ) | ||||
| { | ||||
|     for ( int i = 0; i < children.count(); ++i ) | ||||
|     { | ||||
|         if ( !children.at( i ).isElement() ) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         QDomElement e = children.at( i ).toElement(); | ||||
|         if ( e.tagName() != tagname ) | ||||
|         { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         m[ mapname + getLanguage( e ) ] = e.text(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** @brief gets the <name> and <description> elements | ||||
| * | ||||
| * Builds up a map of the <name> elements (which may have a *lang* | ||||
| * attribute to indicate translations and paragraphs of the | ||||
| * <description> element (also with lang). Uses the <summary> | ||||
| * elements to supplement the description if no description | ||||
| * is available for a given language. | ||||
| * | ||||
| * Returns a map with keys suitable for use by TranslatedString. | ||||
| */ | ||||
| static inline QVariantMap | ||||
| getNameAndSummary( const QDomNode& n ) | ||||
| { | ||||
|     QVariantMap m; | ||||
|  | ||||
|     const QDomNodeList children = n.childNodes(); | ||||
|     fillMap( m, children, "name", "name" ); | ||||
|     fillMap( m, children, "summary", "description" ); | ||||
|  | ||||
|     const QDomElement description = n.firstChildElement( "description" ); | ||||
|     if ( !description.isNull() ) | ||||
|     { | ||||
|         fillMap( m, description.childNodes(), "p", "description" ); | ||||
|     } | ||||
|  | ||||
|     return m; | ||||
| } | ||||
|  | ||||
| PackageItem | ||||
| fromAppData( const QVariantMap& item_map ) | ||||
| { | ||||
|     QString fileName = Calamares::getString( item_map, "appdata" ); | ||||
|     if ( fileName.isEmpty() ) | ||||
|     { | ||||
|         cWarning() << "Can't load AppData without a suitable key."; | ||||
|         return PackageItem(); | ||||
|     } | ||||
|     cDebug() << "Loading AppData XML from" << fileName; | ||||
|  | ||||
|     QDomDocument doc = loadAppData( fileName ); | ||||
|     if ( doc.isNull() ) | ||||
|     { | ||||
|         return PackageItem(); | ||||
|     } | ||||
|  | ||||
|     QDomElement componentNode = doc.documentElement(); | ||||
|     if ( !componentNode.isNull() && componentNode.tagName() == "component" ) | ||||
|     { | ||||
|         // An "id" entry in the Calamares config overrides ID in the AppData | ||||
|         QString id = Calamares::getString( item_map, "id" ); | ||||
|         if ( id.isEmpty() ) | ||||
|         { | ||||
|             id = getChildText( componentNode, "id" ); | ||||
|         } | ||||
|         if ( id.isEmpty() ) | ||||
|         { | ||||
|             return PackageItem(); | ||||
|         } | ||||
|  | ||||
|         // A "screenshot" entry in the Calamares config overrides AppData | ||||
|         QString screenshotPath = Calamares::getString( item_map, "screenshot" ); | ||||
|         if ( screenshotPath.isEmpty() ) | ||||
|         { | ||||
|             screenshotPath = getScreenshotPath( componentNode ); | ||||
|         } | ||||
|  | ||||
|         QVariantMap map = getNameAndSummary( componentNode ); | ||||
|         map.insert( "id", id ); | ||||
|         map.insert( "screenshot", screenshotPath ); | ||||
|  | ||||
|         return PackageItem( map ); | ||||
|     } | ||||
|  | ||||
|     return PackageItem(); | ||||
| } | ||||
							
								
								
									
										28
									
								
								modules/packagechooser/ItemAppData.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef ITEMAPPDATA_H | ||||
| #define ITEMAPPDATA_H | ||||
|  | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| /** @brief Loads an AppData XML file and returns a PackageItem | ||||
|  * | ||||
|  * The @p map must have a key *appdata*. That is used as the | ||||
|  * primary source of information, but keys *id* and *screenshotPath* | ||||
|  * may be used to override parts of the AppData -- so that the | ||||
|  * ID is under the control of Calamares, and the screenshot can be | ||||
|  * forced to a local path available on the installation medium. | ||||
|  * | ||||
|  * Requires XML support in libcalamares, if not present will | ||||
|  * return invalid PackageItems. | ||||
|  */ | ||||
| PackageItem fromAppData( const QVariantMap& map ); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										169
									
								
								modules/packagechooser/ItemAppStream.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,169 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| /** @brief Loading items from AppData XML files. | ||||
|  * | ||||
|  * Only used if QtXML is found, implements PackageItem::fromAppData(). | ||||
|  */ | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| #include "locale/TranslationsModel.h" | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| #include <AppStreamQt/image.h> | ||||
| #include <AppStreamQt/pool.h> | ||||
| #include <AppStreamQt/screenshot.h> | ||||
|  | ||||
| /// @brief Return number of pixels in a size, for < ordering purposes | ||||
| static inline quint64 | ||||
| sizeOrder( const QSize& size ) | ||||
| { | ||||
|     return static_cast<quint64>(size.width()) * static_cast<quint64>(size.height()); | ||||
| } | ||||
|  | ||||
| /// @brief Sets a screenshot in @p map from @p screenshot, if a usable one is found | ||||
| static void | ||||
| setScreenshot( QVariantMap& map, const AppStream::Screenshot& screenshot ) | ||||
| { | ||||
|     if ( screenshot.images().count() < 1 ) | ||||
|     { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Pick the smallest | ||||
|     QUrl url; | ||||
|     quint64 size = sizeOrder( screenshot.images().first().size() ); | ||||
|     for ( const auto& img : screenshot.images() ) | ||||
|     { | ||||
|         if ( sizeOrder( img.size() ) <= size ) | ||||
|         { | ||||
|             url = img.url(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if ( url.isValid() ) | ||||
|     { | ||||
|         map.insert( "screenshot", url.toString() ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// @brief Interpret an AppStream Component | ||||
| static PackageItem | ||||
| fromComponent( AppStream::Pool& pool, AppStream::Component& component ) | ||||
| { | ||||
| #if HAVE_APPSTREAM_VERSION == 0 | ||||
|     auto setActiveLocale = [&component](const QString & locale) | ||||
|     { | ||||
|         component.setActiveLocale( locale ); | ||||
|     }; | ||||
| #else | ||||
|     auto setActiveLocale = [&pool](const QString & locale) | ||||
|     { | ||||
|         pool.setLocale( locale ); | ||||
|     }; | ||||
| #endif | ||||
|  | ||||
|     QVariantMap map; | ||||
|     map.insert( "id", component.id() ); | ||||
|     map.insert( "package", component.packageNames().join( "," ) ); | ||||
|  | ||||
|     // Assume that the pool has loaded "ALL" locales, but it might be set | ||||
|     // to any of them; get the en_US locale as "untranslated" and then | ||||
|     // loop over Calamares locales (since there is no way to query for | ||||
|     // available locales in the Component) to see if there's anything else. | ||||
|     setActiveLocale( QStringLiteral( "en_US" ) ); | ||||
|     QString en_name = component.name(); | ||||
|     QString en_description = component.description(); | ||||
|     map.insert( "name", en_name ); | ||||
|     map.insert( "description", en_description ); | ||||
|  | ||||
|     for ( const QString& locale : Calamares::Locale::availableTranslations()->localeIds() ) | ||||
|     { | ||||
|         setActiveLocale( locale ); | ||||
|         QString name = component.name(); | ||||
|         if ( name != en_name ) | ||||
|         { | ||||
|             map.insert( QStringLiteral( "name[%1]" ).arg( locale ), name ); | ||||
|         } | ||||
|         QString description = component.description(); | ||||
|         if ( description != en_description ) | ||||
|         { | ||||
|             map.insert( QStringLiteral( "description[%1]" ).arg( locale ), description ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| #if HAVE_APPSTREAM_VERSION == 0 | ||||
|     auto screenshots = component.screenshots(); | ||||
| #else | ||||
|     auto screenshots = component.screenshotsAll(); | ||||
| #endif | ||||
|     if ( screenshots.count() > 0 ) | ||||
|     { | ||||
|         bool done = false; | ||||
|         for ( const auto& s : screenshots ) | ||||
|         { | ||||
|             if ( s.isDefault() ) | ||||
|             { | ||||
|                 setScreenshot( map, s ); | ||||
|                 done = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if ( !done ) | ||||
|         { | ||||
|             setScreenshot( map, screenshots.first() ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return PackageItem( map ); | ||||
| } | ||||
|  | ||||
| PackageItem | ||||
| fromAppStream( AppStream::Pool& pool, const QVariantMap& item_map ) | ||||
| { | ||||
|     QString appstreamId = Calamares::getString( item_map, "appstream" ); | ||||
|     if ( appstreamId.isEmpty() ) | ||||
|     { | ||||
|         cWarning() << "Can't load AppStream without a suitable appstreamId."; | ||||
|         return PackageItem(); | ||||
|     } | ||||
|     cDebug() << "Loading AppStream data for" << appstreamId; | ||||
|  | ||||
| #if HAVE_APPSTREAM_VERSION == 0 | ||||
|     auto itemList = pool.componentsById( appstreamId ); | ||||
| #else | ||||
|     auto itemList = pool.componentsById( appstreamId ).toList(); | ||||
| #endif | ||||
|     if ( itemList.count() < 1 ) | ||||
|     { | ||||
|         cWarning() << "No AppStream data for" << appstreamId; | ||||
|         return PackageItem(); | ||||
|     } | ||||
|     if ( itemList.count() > 1 ) | ||||
|     { | ||||
|         cDebug() << "Multiple AppStream data for" << appstreamId << "using first."; | ||||
|     } | ||||
|  | ||||
|     auto r = fromComponent( pool, itemList.first() ); | ||||
|     if ( r.isValid() ) | ||||
|     { | ||||
|         QString id = Calamares::getString( item_map, "id" ); | ||||
|         QString screenshotPath = Calamares::getString( item_map, "screenshot" ); | ||||
|         if ( !id.isEmpty() ) | ||||
|         { | ||||
|             r.id = id; | ||||
|         } | ||||
|         if ( !screenshotPath.isEmpty() ) | ||||
|         { | ||||
|             r.screenshot = screenshotPath; | ||||
|         } | ||||
|     } | ||||
|     return r; | ||||
| } | ||||
							
								
								
									
										34
									
								
								modules/packagechooser/ItemAppStream.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef ITEMAPPSTREAM_H | ||||
| #define ITEMAPPSTREAM_H | ||||
|  | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| namespace AppStream | ||||
| { | ||||
| class Pool; | ||||
| }  // namespace AppStream | ||||
|  | ||||
| /** @brief Loads an item from AppStream data. | ||||
|  * | ||||
|  * The @p map must have a key *appstream*. That is used as the | ||||
|  * primary source of information from the AppStream cache, but | ||||
|  * keys *id* and *screenshotPath* may be used to override parts | ||||
|  * of the AppStream data -- so that the ID is under the control | ||||
|  * of Calamares, and the screenshot can be forced to a local path | ||||
|  * available on the installation medium. | ||||
|  * | ||||
|  * Requires AppStreamQt, if not present will return invalid | ||||
|  * PackageItems. | ||||
|  */ | ||||
| PackageItem fromAppStream( AppStream::Pool& pool, const QVariantMap& map ); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										145
									
								
								modules/packagechooser/PackageChooserPage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,145 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "PackageChooserPage.h" | ||||
|  | ||||
| #include "ui_page_package.h" | ||||
|  | ||||
| #include "utils/Gui.h" | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/Retranslator.h" | ||||
|  | ||||
| #include <QLabel> | ||||
|  | ||||
| PackageChooserPage::PackageChooserPage( PackageChooserMode mode, QWidget* parent ) | ||||
|     : QWidget( parent ) | ||||
|     , ui( new Ui::PackageChooserPage ) | ||||
|     , m_introduction( QString(), | ||||
|                       QString(), | ||||
|                       tr( "Package Selection" ), | ||||
|                       tr( "Please pick a product from the list. The selected product will be installed." ) ) | ||||
| { | ||||
|     m_introduction.screenshot = QPixmap( QStringLiteral( ":/images/no-selection.png" ) ); | ||||
|  | ||||
|     ui->setupUi( this ); | ||||
|     CALAMARES_RETRANSLATE( updateLabels(); ); | ||||
|  | ||||
|     switch ( mode ) | ||||
|     { | ||||
|     case PackageChooserMode::Optional: | ||||
|         [[fallthrough]]; | ||||
|     case PackageChooserMode::Required: | ||||
|         ui->products->setSelectionMode( QAbstractItemView::SingleSelection ); | ||||
|         break; | ||||
|     case PackageChooserMode::OptionalMultiple: | ||||
|         [[fallthrough]]; | ||||
|     case PackageChooserMode::RequiredMultiple: | ||||
|         ui->products->setSelectionMode( QAbstractItemView::ExtendedSelection ); | ||||
|     } | ||||
|  | ||||
|     ui->products->setMinimumWidth( 10 * Calamares::defaultFontHeight() ); | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserPage::currentChanged( const QModelIndex& index ) | ||||
| { | ||||
|     if ( !index.isValid() || !ui->products->selectionModel()->hasSelection() ) | ||||
|     { | ||||
|         ui->productName->setText( m_introduction.name.get() ); | ||||
|         ui->productScreenshot->setPixmap( m_introduction.screenshot ); | ||||
|         ui->productDescription->setText( m_introduction.description.get() ); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         const auto* model = ui->products->model(); | ||||
|  | ||||
|         ui->productName->setText( model->data( index, PackageListModel::NameRole ).toString() ); | ||||
|         ui->productDescription->setText( model->data( index, PackageListModel::DescriptionRole ).toString() ); | ||||
|  | ||||
|         QPixmap currentScreenshot = model->data( index, PackageListModel::ScreenshotRole ).value< QPixmap >(); | ||||
|         if ( currentScreenshot.isNull() ) | ||||
|         { | ||||
|             ui->productScreenshot->setPixmap( m_introduction.screenshot ); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             ui->productScreenshot->setPixmap( currentScreenshot ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserPage::updateLabels() | ||||
| { | ||||
|     if ( ui && ui->products && ui->products->selectionModel() ) | ||||
|     { | ||||
|         currentChanged( ui->products->selectionModel()->currentIndex() ); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         currentChanged( QModelIndex() ); | ||||
|     } | ||||
|     emit selectionChanged(); | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserPage::setModel( QAbstractItemModel* model ) | ||||
| { | ||||
|     ui->products->setModel( model ); | ||||
|     currentChanged( QModelIndex() ); | ||||
|     connect( ui->products->selectionModel(), | ||||
|              &QItemSelectionModel::selectionChanged, | ||||
|              this, | ||||
|              &PackageChooserPage::updateLabels ); | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserPage::setSelection( const QModelIndex& index ) | ||||
| { | ||||
|     if ( index.isValid() ) | ||||
|     { | ||||
|         ui->products->selectionModel()->select( index, QItemSelectionModel::Select ); | ||||
|     } | ||||
|     currentChanged( index ); | ||||
| } | ||||
|  | ||||
| bool | ||||
| PackageChooserPage::hasSelection() const | ||||
| { | ||||
|     return ui && ui->products && ui->products->selectionModel() && ui->products->selectionModel()->hasSelection(); | ||||
| } | ||||
|  | ||||
| QStringList | ||||
| PackageChooserPage::selectedPackageIds() const | ||||
| { | ||||
|     if ( !( ui && ui->products && ui->products->selectionModel() ) ) | ||||
|     { | ||||
|         return QStringList(); | ||||
|     } | ||||
|  | ||||
|     const auto* model = ui->products->model(); | ||||
|     QStringList ids; | ||||
|     for ( const auto& index : ui->products->selectionModel()->selectedIndexes() ) | ||||
|     { | ||||
|         QString pid = model->data( index, PackageListModel::IdRole ).toString(); | ||||
|         if ( !pid.isEmpty() ) | ||||
|         { | ||||
|             ids.append( pid ); | ||||
|         } | ||||
|     } | ||||
|     return ids; | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserPage::setIntroduction( const PackageItem& item ) | ||||
| { | ||||
|     m_introduction.name = item.name; | ||||
|     m_introduction.description = item.description; | ||||
|     m_introduction.screenshot = item.screenshot; | ||||
| } | ||||
							
								
								
									
										57
									
								
								modules/packagechooser/PackageChooserPage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,57 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef PACKAGECHOOSERPAGE_H | ||||
| #define PACKAGECHOOSERPAGE_H | ||||
|  | ||||
| #include "Config.h" | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| #include <QAbstractItemModel> | ||||
| #include <QWidget> | ||||
|  | ||||
| namespace Ui | ||||
| { | ||||
| class PackageChooserPage; | ||||
| }  // namespace Ui | ||||
|  | ||||
| class PackageChooserPage : public QWidget | ||||
| { | ||||
|     Q_OBJECT | ||||
| public: | ||||
|     explicit PackageChooserPage( PackageChooserMode mode, QWidget* parent = nullptr ); | ||||
|  | ||||
|     /// @brief Sets the data model for the listview | ||||
|     void setModel( QAbstractItemModel* model ); | ||||
|  | ||||
|     /// @brief Sets the introductory (no-package-selected) texts | ||||
|     void setIntroduction( const PackageItem& item ); | ||||
|     /// @brief Selects a listview item | ||||
|     void setSelection( const QModelIndex& index ); | ||||
|     /// @brief Is anything selected? | ||||
|     bool hasSelection() const; | ||||
|     /** @brief Get the list of selected ids | ||||
|      * | ||||
|      * This list may be empty (if none is selected). | ||||
|      */ | ||||
|     QStringList selectedPackageIds() const; | ||||
|  | ||||
| public slots: | ||||
|     void currentChanged( const QModelIndex& index ); | ||||
|     void updateLabels(); | ||||
|  | ||||
| signals: | ||||
|     void selectionChanged(); | ||||
|  | ||||
| private: | ||||
|     Ui::PackageChooserPage* ui; | ||||
|     PackageItem m_introduction; | ||||
| }; | ||||
|  | ||||
| #endif  // PACKAGECHOOSERPAGE_H | ||||
							
								
								
									
										158
									
								
								modules/packagechooser/PackageChooserViewStep.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,158 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "PackageChooserViewStep.h" | ||||
|  | ||||
| #include "Config.h" | ||||
| #include "PackageChooserPage.h" | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| #include "GlobalStorage.h" | ||||
| #include "JobQueue.h" | ||||
| #include "locale/TranslatableConfiguration.h" | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/System.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| #include <QDesktopServices> | ||||
| #include <QVariantMap> | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DEFINITION( PackageChooserViewStepFactory, registerPlugin< PackageChooserViewStep >(); ) | ||||
|  | ||||
| PackageChooserViewStep::PackageChooserViewStep( QObject* parent ) | ||||
|     : Calamares::ViewStep( parent ) | ||||
|     , m_config( new Config( this ) ) | ||||
|     , m_widget( nullptr ) | ||||
| { | ||||
|     emit nextStatusChanged( false ); | ||||
| } | ||||
|  | ||||
|  | ||||
| PackageChooserViewStep::~PackageChooserViewStep() | ||||
| { | ||||
|     if ( m_widget && m_widget->parent() == nullptr ) | ||||
|     { | ||||
|         m_widget->deleteLater(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| QString | ||||
| PackageChooserViewStep::prettyName() const | ||||
| { | ||||
|     return m_config->prettyName(); | ||||
| } | ||||
|  | ||||
|  | ||||
| QWidget* | ||||
| PackageChooserViewStep::widget() | ||||
| { | ||||
|     if ( !m_widget ) | ||||
|     { | ||||
|         m_widget = new PackageChooserPage( m_config->mode(), nullptr ); | ||||
|         connect( m_widget, | ||||
|                  &PackageChooserPage::selectionChanged, | ||||
|                  [ = ]() { emit nextStatusChanged( this->isNextEnabled() ); } ); | ||||
|         hookupModel(); | ||||
|     } | ||||
|     return m_widget; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool | ||||
| PackageChooserViewStep::isNextEnabled() const | ||||
| { | ||||
|     if ( !m_widget ) | ||||
|     { | ||||
|         // No way to have changed anything | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     switch ( m_config->mode() ) | ||||
|     { | ||||
|     case PackageChooserMode::Optional: | ||||
|     case PackageChooserMode::OptionalMultiple: | ||||
|         // zero or one OR zero or more | ||||
|         return true; | ||||
|     case PackageChooserMode::Required: | ||||
|     case PackageChooserMode::RequiredMultiple: | ||||
|         // exactly one OR one or more | ||||
|         return m_widget->hasSelection(); | ||||
|     } | ||||
|     __builtin_unreachable(); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool | ||||
| PackageChooserViewStep::isBackEnabled() const | ||||
| { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool | ||||
| PackageChooserViewStep::isAtBeginning() const | ||||
| { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool | ||||
| PackageChooserViewStep::isAtEnd() const | ||||
| { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserViewStep::onActivate() | ||||
| { | ||||
|     if ( !m_widget->hasSelection() ) | ||||
|     { | ||||
|         m_widget->setSelection( m_config->defaultSelectionIndex() ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserViewStep::onLeave() | ||||
| { | ||||
|     m_config->updateGlobalStorage( m_widget->selectedPackageIds() ); | ||||
| } | ||||
|  | ||||
| Calamares::JobList | ||||
| PackageChooserViewStep::jobs() const | ||||
| { | ||||
|     Calamares::JobList l; | ||||
|     return l; | ||||
| } | ||||
|  | ||||
| void | ||||
| PackageChooserViewStep::setConfigurationMap( const QVariantMap& configurationMap ) | ||||
| { | ||||
|     m_config->setDefaultId( moduleInstanceKey() ); | ||||
|     m_config->setConfigurationMap( configurationMap ); | ||||
|  | ||||
|     if ( m_widget ) | ||||
|     { | ||||
|         hookupModel(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| void | ||||
| PackageChooserViewStep::hookupModel() | ||||
| { | ||||
|     if ( !m_config->model() || !m_widget ) | ||||
|     { | ||||
|         cError() << "Can't hook up model until widget and model both exist."; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     m_widget->setModel( m_config->model() ); | ||||
|     m_widget->setIntroduction( m_config->introductionPackage() ); | ||||
| } | ||||
							
								
								
									
										57
									
								
								modules/packagechooser/PackageChooserViewStep.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,57 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef PACKAGECHOOSERVIEWSTEP_H | ||||
| #define PACKAGECHOOSERVIEWSTEP_H | ||||
|  | ||||
| #include "DllMacro.h" | ||||
| #include "locale/TranslatableConfiguration.h" | ||||
| #include "utils/PluginFactory.h" | ||||
| #include "viewpages/ViewStep.h" | ||||
|  | ||||
| #include <QVariantMap> | ||||
|  | ||||
| class Config; | ||||
| class PackageChooserPage; | ||||
|  | ||||
| class PLUGINDLLEXPORT PackageChooserViewStep : public Calamares::ViewStep | ||||
| { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit PackageChooserViewStep( QObject* parent = nullptr ); | ||||
|     ~PackageChooserViewStep() override; | ||||
|  | ||||
|     QString prettyName() const override; | ||||
|  | ||||
|     QWidget* widget() override; | ||||
|  | ||||
|     bool isNextEnabled() const override; | ||||
|     bool isBackEnabled() const override; | ||||
|  | ||||
|     bool isAtBeginning() const override; | ||||
|     bool isAtEnd() const override; | ||||
|  | ||||
|     void onActivate() override; | ||||
|     void onLeave() override; | ||||
|  | ||||
|     Calamares::JobList jobs() const override; | ||||
|  | ||||
|     void setConfigurationMap( const QVariantMap& configurationMap ) override; | ||||
|  | ||||
| private: | ||||
|     void hookupModel(); | ||||
|  | ||||
|     Config* m_config; | ||||
|     PackageChooserPage* m_widget; | ||||
| }; | ||||
|  | ||||
| CALAMARES_PLUGIN_FACTORY_DECLARATION( PackageChooserViewStepFactory ) | ||||
|  | ||||
| #endif  // PACKAGECHOOSERVIEWSTEP_H | ||||
							
								
								
									
										196
									
								
								modules/packagechooser/PackageModel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,196 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "PackageModel.h" | ||||
|  | ||||
| #include "Branding.h" | ||||
| #include "utils/Logger.h" | ||||
| #include "utils/Variant.h" | ||||
|  | ||||
| #include <QFileInfo> | ||||
|  | ||||
| /** @brief A wrapper for Calamares::getSubMap that excludes the success param | ||||
|  */ | ||||
| static QVariantMap | ||||
| getSubMap( const QVariantMap& map, const QString& key ) | ||||
| { | ||||
|     bool success; | ||||
|  | ||||
|     return Calamares::getSubMap( map, key, success ); | ||||
| } | ||||
|  | ||||
| static QPixmap | ||||
| loadScreenshot( const QString& path ) | ||||
| { | ||||
|     if ( QFileInfo::exists( path ) ) | ||||
|     { | ||||
|         return QPixmap( path ); | ||||
|     } | ||||
|  | ||||
|     const auto* branding = Calamares::Branding::instance(); | ||||
|     if ( !branding ) | ||||
|     { | ||||
|         return QPixmap(); | ||||
|     } | ||||
|     return QPixmap( branding->componentDirectory() + QStringLiteral( "/" ) + path ); | ||||
| } | ||||
|  | ||||
| PackageItem::PackageItem() {} | ||||
|  | ||||
| PackageItem::PackageItem( const QString& a_id, const QString& a_name, const QString& a_description ) | ||||
|     : id( a_id ) | ||||
|     , name( a_name ) | ||||
|     , description( a_description ) | ||||
| { | ||||
| } | ||||
|  | ||||
| PackageItem::PackageItem( const QString& a_id, | ||||
|                           const QString& a_name, | ||||
|                           const QString& a_description, | ||||
|                           const QString& screenshotPath ) | ||||
|     : id( a_id ) | ||||
|     , name( a_name ) | ||||
|     , description( a_description ) | ||||
|     , screenshot( screenshotPath ) | ||||
| { | ||||
| } | ||||
|  | ||||
| PackageItem::PackageItem( const QVariantMap& item_map ) | ||||
|     : id( Calamares::getString( item_map, "id" ) ) | ||||
|     , name( Calamares::Locale::TranslatedString( item_map, "name" ) ) | ||||
|     , description( Calamares::Locale::TranslatedString( item_map, "description" ) ) | ||||
|     , screenshot( loadScreenshot( Calamares::getString( item_map, "screenshot" ) ) ) | ||||
|     , packageNames( Calamares::getStringList( item_map, "packages" ) ) | ||||
|     , netinstallData( getSubMap( item_map, "netinstall" ) ) | ||||
| { | ||||
|     if ( name.isEmpty() && id.isEmpty() ) | ||||
|     { | ||||
|         name = QObject::tr( "No product" ); | ||||
|     } | ||||
|     else if ( name.isEmpty() ) | ||||
|     { | ||||
|         cWarning() << "PackageChooser item" << id << "has an empty name."; | ||||
|     } | ||||
|     if ( description.isEmpty() ) | ||||
|     { | ||||
|         description = QObject::tr( "No description provided." ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| PackageListModel::PackageListModel( QObject* parent ) | ||||
|     : QAbstractListModel( parent ) | ||||
| { | ||||
| } | ||||
|  | ||||
| PackageListModel::PackageListModel( PackageList&& items, QObject* parent ) | ||||
|     : QAbstractListModel( parent ) | ||||
|     , m_packages( std::move( items ) ) | ||||
| { | ||||
| } | ||||
|  | ||||
| PackageListModel::~PackageListModel() {} | ||||
|  | ||||
| void | ||||
| PackageListModel::addPackage( PackageItem&& p ) | ||||
| { | ||||
|     // Only add valid packages | ||||
|     if ( p.isValid() ) | ||||
|     { | ||||
|         int c = m_packages.count(); | ||||
|         beginInsertRows( QModelIndex(), c, c ); | ||||
|         m_packages.append( p ); | ||||
|         endInsertRows(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| QStringList | ||||
| PackageListModel::getInstallPackagesForName( const QString& id ) const | ||||
| { | ||||
|     for ( const auto& p : qAsConst( m_packages ) ) | ||||
|     { | ||||
|         if ( p.id == id ) | ||||
|         { | ||||
|             return p.packageNames; | ||||
|         } | ||||
|     } | ||||
|     return QStringList(); | ||||
| } | ||||
|  | ||||
| QStringList | ||||
| PackageListModel::getInstallPackagesForNames( const QStringList& ids ) const | ||||
| { | ||||
|     QStringList l; | ||||
|     for ( const auto& p : qAsConst( m_packages ) ) | ||||
|     { | ||||
|         if ( ids.contains( p.id ) ) | ||||
|         { | ||||
|             l.append( p.packageNames ); | ||||
|         } | ||||
|     } | ||||
|     return l; | ||||
| } | ||||
|  | ||||
| QVariantList | ||||
| PackageListModel::getNetinstallDataForNames( const QStringList& ids ) const | ||||
| { | ||||
|     QVariantList l; | ||||
|     for ( auto& p : m_packages ) | ||||
|     { | ||||
|         if ( ids.contains( p.id ) ) | ||||
|         { | ||||
|             if ( !p.netinstallData.isEmpty() ) | ||||
|             { | ||||
|                 QVariantMap newData = p.netinstallData; | ||||
|                 newData[ "source" ] = QStringLiteral( "packageChooser" ); | ||||
|                 l.append( newData ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return l; | ||||
| } | ||||
|  | ||||
| int | ||||
| PackageListModel::rowCount( const QModelIndex& index ) const | ||||
| { | ||||
|     // For lists, valid indexes have zero children; only the root index has them | ||||
|     return index.isValid() ? 0 : m_packages.count(); | ||||
| } | ||||
|  | ||||
| QVariant | ||||
| PackageListModel::data( const QModelIndex& index, int role ) const | ||||
| { | ||||
|     if ( !index.isValid() ) | ||||
|     { | ||||
|         return QVariant(); | ||||
|     } | ||||
|     int row = index.row(); | ||||
|     if ( row >= m_packages.count() || row < 0 ) | ||||
|     { | ||||
|         return QVariant(); | ||||
|     } | ||||
|  | ||||
|     if ( role == Qt::DisplayRole /* Also PackageNameRole */ ) | ||||
|     { | ||||
|         return m_packages[ row ].name.get(); | ||||
|     } | ||||
|     else if ( role == DescriptionRole ) | ||||
|     { | ||||
|         return m_packages[ row ].description.get(); | ||||
|     } | ||||
|     else if ( role == ScreenshotRole ) | ||||
|     { | ||||
|         return m_packages[ row ].screenshot; | ||||
|     } | ||||
|     else if ( role == IdRole ) | ||||
|     { | ||||
|         return m_packages[ row ].id; | ||||
|     } | ||||
|  | ||||
|     return QVariant(); | ||||
| } | ||||
							
								
								
									
										135
									
								
								modules/packagechooser/PackageModel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,135 @@ | ||||
| /* === This file is part of Calamares - <https://calamares.io> === | ||||
|  * | ||||
|  *   SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org> | ||||
|  *   SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  * | ||||
|  *   Calamares is Free Software: see the License-Identifier above. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef PACKAGEMODEL_H | ||||
| #define PACKAGEMODEL_H | ||||
|  | ||||
| #include "locale/TranslatableConfiguration.h" | ||||
| #include "utils/NamedEnum.h" | ||||
|  | ||||
| #include <QAbstractListModel> | ||||
| #include <QObject> | ||||
| #include <QPixmap> | ||||
| #include <QVector> | ||||
|  | ||||
|  | ||||
| struct PackageItem | ||||
| { | ||||
|     QString id; | ||||
|     Calamares::Locale::TranslatedString name; | ||||
|     Calamares::Locale::TranslatedString description; | ||||
|     QPixmap screenshot; | ||||
|     QStringList packageNames; | ||||
|     QVariantMap netinstallData; | ||||
|  | ||||
|     /// @brief Create blank PackageItem | ||||
|     PackageItem(); | ||||
|     /** @brief Creates a PackageItem from given strings | ||||
|      * | ||||
|      * This constructor sets all the text members, | ||||
|      * but leaves the screenshot blank. Set that separately. | ||||
|      */ | ||||
|     PackageItem( const QString& id, const QString& name, const QString& description ); | ||||
|  | ||||
|     /** @brief Creates a PackageItem from given strings. | ||||
|      * | ||||
|      * Set all the text members and load the screenshot from the given | ||||
|      * @p screenshotPath, which may be a QRC path (:/path/in/qrc) or | ||||
|      * a filesystem path, whatever QPixmap understands. | ||||
|      */ | ||||
|     PackageItem( const QString& id, const QString& name, const QString& description, const QString& screenshotPath ); | ||||
|  | ||||
|     /** @brief Creates a PackageItem from a QVariantMap | ||||
|      * | ||||
|      * This is intended for use when loading PackageItems from a | ||||
|      * configuration map. It will look up the various keys in the map | ||||
|      * and handle translation strings as well. | ||||
|      * | ||||
|      * The following keys are used: | ||||
|      *  - *id*: the identifier for this item; if it is the empty string | ||||
|      *    then this is the special "no-package". | ||||
|      *  - *name* (and *name[lang]*): for the name and its translations | ||||
|      *  - *description* (and *description[lang]*) | ||||
|      *  - *screenshot*: a path to a screenshot for this package | ||||
|      *  - *packages*: a list of package names | ||||
|      */ | ||||
|     PackageItem( const QVariantMap& map ); | ||||
|  | ||||
|     /** @brief Is this item valid? | ||||
|      * | ||||
|      * A valid item has an untranslated name available. | ||||
|      */ | ||||
|     bool isValid() const { return !name.isEmpty(); } | ||||
|  | ||||
|     /** @brief Is this a (the) No-Package package? | ||||
|      * | ||||
|      * There should be at most one No-Package item in a collection | ||||
|      * of PackageItems. That one will be used to describe a | ||||
|      * "no package" situation. | ||||
|      */ | ||||
|     bool isNonePackage() const { return id.isEmpty(); } | ||||
| }; | ||||
|  | ||||
| using PackageList = QVector< PackageItem >; | ||||
|  | ||||
| class PackageListModel : public QAbstractListModel | ||||
| { | ||||
| public: | ||||
|     PackageListModel( PackageList&& items, QObject* parent ); | ||||
|     PackageListModel( QObject* parent ); | ||||
|     ~PackageListModel() override; | ||||
|  | ||||
|     /** @brief Add a package @p to the model | ||||
|      * | ||||
|      * Only valid packages are added -- that is, they must have a name. | ||||
|      */ | ||||
|     void addPackage( PackageItem&& p ); | ||||
|  | ||||
|     int rowCount( const QModelIndex& index ) const override; | ||||
|     QVariant data( const QModelIndex& index, int role ) const override; | ||||
|  | ||||
|     /// @brief Direct (non-abstract) access to package data | ||||
|     const PackageItem& packageData( int r ) const { return m_packages[ r ]; } | ||||
|     /// @brief Direct (non-abstract) count of package data | ||||
|     int packageCount() const { return m_packages.count(); } | ||||
|  | ||||
|     /** @brief Does a name lookup (based on id) and returns the packages member | ||||
|      * | ||||
|      * If there is a package with the given @p id, returns its packages | ||||
|      * (e.g. the names of underlying packages to install for it); returns | ||||
|      * an empty list if the id is not found. | ||||
|      */ | ||||
|     QStringList getInstallPackagesForName( const QString& id ) const; | ||||
|     /** @brief Name-lookup all the @p ids and returns the packages members | ||||
|      * | ||||
|      * Concatenates installPackagesForName() for each id in @p ids. | ||||
|      */ | ||||
|     QStringList getInstallPackagesForNames( const QStringList& ids ) const; | ||||
|  | ||||
|     /** @brief Does a name lookup (based on id) and returns the netinstall data | ||||
|      * | ||||
|      * If there is a package with an id in @p ids, returns their netinstall data | ||||
|      * | ||||
|      * returns a list of netinstall data or an emply list if none is found | ||||
|      */ | ||||
|     QVariantList getNetinstallDataForNames( const QStringList& ids ) const; | ||||
|  | ||||
|     enum Roles : int | ||||
|     { | ||||
|         NameRole = Qt::DisplayRole, | ||||
|         DescriptionRole = Qt::UserRole, | ||||
|         ScreenshotRole, | ||||
|         IdRole | ||||
|     }; | ||||
|  | ||||
| private: | ||||
|     PackageList m_packages; | ||||
| }; | ||||
|  | ||||
| #endif | ||||