From 4528e4428690ab0886f82eca43e3101c7edf58e0 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Mon, 3 Feb 2025 11:50:34 +0000 Subject: [PATCH 01/47] .gitignore: Add vscode related files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 67a3574e..db5e3750 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ src/build/* src/build/** .vscode +compile_commands.json +src/.cache/clangd obj-** debian/rpi-imager/** debian/.debhelper** From fb55183b50b3685eafe52449c1580c83a0ba6e4b Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Mon, 3 Feb 2025 15:50:14 +0000 Subject: [PATCH 02/47] Introduce qmlcomponents/ImPopup.qml UseSavedSettingsPopup.qml and MsgPopup.qml now inherit from it. Reduces code duplication, hardcoded duplicated sizes, etc. MsgPopup now also gets a "pointing hand" cursor for its close button, didn't have before due to oversight, now it inherits from ImPopup. --- src/MsgPopup.qml | 159 ++++++++++---------------------- src/UseSavedSettingsPopup.qml | 169 +++++++++++----------------------- src/qml.qrc | 1 + src/qmlcomponents/ImPopup.qml | 86 +++++++++++++++++ 4 files changed, 191 insertions(+), 224 deletions(-) create mode 100644 src/qmlcomponents/ImPopup.qml diff --git a/src/MsgPopup.qml b/src/MsgPopup.qml index 7539fa5e..0d7e2939 100644 --- a/src/MsgPopup.qml +++ b/src/MsgPopup.qml @@ -9,139 +9,80 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 import "qmlcomponents" -Popup { - id: msgpopup - x: (parent.width-width)/2 - y: (parent.height-height)/2 - width: 550 - height: msgpopupbody.implicitHeight+150 - padding: 0 +ImPopup { + id: root closePolicy: Popup.CloseOnEscape - modal: true - property alias title: msgpopupheader.text property alias text: msgpopupbody.text property bool continueButton: true property bool quitButton: false property bool yesButton: false property bool noButton: false - signal yes() - signal no() - // background of title - Rectangle { - id: msgpopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width + height: msgpopupbody.implicitHeight+150 - Text { - id: msgpopupheader - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: roboto.name - font.bold: true - } + // These children go into ImPopup's ColumnLayout - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: roboto.name - font.bold: true - - MouseArea { - anchors.fill: parent - onClicked: { - msgpopup.close() - } - } - } - } - // line under title - Rectangle { - id: msgpopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: msgpopup_title_background.bottom - height: 1 + Text { + id: msgpopupbody + font.pointSize: 12 + wrapMode: Text.Wrap + textFormat: Text.StyledText + font.family: roboto.name + Layout.fillHeight: true + Layout.fillWidth: true + Layout.leftMargin: 10 + Layout.rightMargin: 10 + Layout.topMargin: 10 + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "") } - ColumnLayout { + RowLayout { + Layout.alignment: Qt.AlignCenter | Qt.AlignBottom + Layout.bottomMargin: 10 spacing: 20 - anchors.top: msgpopup_title_separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom + id: buttons - Text { - id: msgpopupbody - font.pointSize: 12 - wrapMode: Text.Wrap - textFormat: Text.StyledText - font.family: roboto.name - Layout.fillHeight: true - Layout.fillWidth: true - Layout.leftMargin: 10 - Layout.rightMargin: 10 - Layout.topMargin: 10 - Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "") - } - - RowLayout { - Layout.alignment: Qt.AlignCenter | Qt.AlignBottom - Layout.bottomMargin: 10 - spacing: 20 - id: buttons - - ImButtonRed { - text: qsTr("NO") - onClicked: { - msgpopup.close() - msgpopup.no() - } - visible: msgpopup.noButton + ImButtonRed { + text: qsTr("NO") + onClicked: { + root.close() + root.no() } + visible: root.noButton + } - ImButtonRed { - text: qsTr("YES") - onClicked: { - msgpopup.close() - msgpopup.yes() - } - visible: msgpopup.yesButton + ImButtonRed { + text: qsTr("YES") + onClicked: { + root.close() + root.yes() } + visible: root.yesButton + } - ImButtonRed { - text: qsTr("CONTINUE") - onClicked: { - msgpopup.close() - } - visible: msgpopup.continueButton + ImButtonRed { + text: qsTr("CONTINUE") + onClicked: { + root.close() } + visible: root.continueButton + } - ImButtonRed { - text: qsTr("QUIT") - onClicked: { - Qt.quit() - } - font.family: roboto.name - visible: msgpopup.quitButton + ImButtonRed { + text: qsTr("QUIT") + onClicked: { + Qt.quit() } + font.family: roboto.name + visible: root.quitButton } } + function openPopup() { open() // trigger screen reader to speak out message diff --git a/src/UseSavedSettingsPopup.qml b/src/UseSavedSettingsPopup.qml index a81a8697..b6d8c6dc 100644 --- a/src/UseSavedSettingsPopup.qml +++ b/src/UseSavedSettingsPopup.qml @@ -9,138 +9,77 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 import "qmlcomponents" -Popup { - id: msgpopup - x: (parent.width-width)/2 - y: (parent.height-height)/2 - width: 550 +ImPopup { + id: root + height: msgpopupbody.implicitHeight+150 - padding: 0 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - modal: true property bool hasSavedSettings: false - signal yes() - signal no() signal noClearSettings() signal editSettings() signal closeSettings() - // background of title - Rectangle { - id: msgpopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - id: msgpopupheader - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: roboto.name - font.bold: true - text: qsTr("Use OS customization?") - } - - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: roboto.name - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - msgpopup.close() - } - } - } - } - // line under title - Rectangle { - id: msgpopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: msgpopup_title_background.bottom - height: 1 + // These children go into ImPopup's ColumnLayout + + Text { + id: msgpopupbody + font.pointSize: 12 + wrapMode: Text.Wrap + textFormat: Text.StyledText + font.family: roboto.name + Layout.fillHeight: true + Layout.leftMargin: 25 + Layout.rightMargin: 25 + Layout.topMargin: 25 + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "") + text: qsTr("Would you like to apply OS customization settings?") } - ColumnLayout { - spacing: 20 - anchors.top: msgpopup_title_separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - Text { - id: msgpopupbody - font.pointSize: 12 - wrapMode: Text.Wrap - textFormat: Text.StyledText - font.family: roboto.name - Layout.fillHeight: true - Layout.leftMargin: 25 - Layout.rightMargin: 25 - Layout.topMargin: 25 - Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter - Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "") - text: qsTr("Would you like to apply OS customization settings?") - } - - RowLayout { - Layout.alignment: Qt.AlignCenter | Qt.AlignBottom - Layout.bottomMargin: 10 - id: buttons - - ImButtonRed { - text: qsTr("EDIT SETTINGS") - onClicked: { - // Don't close this dialog when "edit settings" is - // clicked, as we don't want the user to fall back to the - // start of the flow. After editing the settings we want - // then to once again have the choice about whether to use - // customisation or not. - msgpopup.editSettings() - } + RowLayout { + Layout.alignment: Qt.AlignCenter | Qt.AlignBottom + Layout.bottomMargin: 10 + id: buttons + + ImButtonRed { + text: qsTr("EDIT SETTINGS") + onClicked: { + // Don't close this dialog when "edit settings" is + // clicked, as we don't want the user to fall back to the + // start of the flow. After editing the settings we want + // then to once again have the choice about whether to use + // customisation or not. + root.editSettings() } + } - ImButtonRed { - id: noAndClearButton - text: qsTr("NO, CLEAR SETTINGS") - onClicked: { - msgpopup.close() - msgpopup.noClearSettings() - } - enabled: hasSavedSettings + ImButtonRed { + id: noAndClearButton + text: qsTr("NO, CLEAR SETTINGS") + onClicked: { + root.close() + root.noClearSettings() } + enabled: hasSavedSettings + } - ImButtonRed { - id: yesButton - text: qsTr("YES") - onClicked: { - msgpopup.close() - msgpopup.yes() - } - enabled: hasSavedSettings + ImButtonRed { + id: yesButton + text: qsTr("YES") + onClicked: { + root.close() + root.yes() } + enabled: hasSavedSettings + } - ImButtonRed { - text: qsTr("NO") - onClicked: { - msgpopup.close() - msgpopup.no() - } + ImButtonRed { + text: qsTr("NO") + onClicked: { + root.close() + root.no() } } } diff --git a/src/qml.qrc b/src/qml.qrc index 8dcca05a..472ba551 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -36,6 +36,7 @@ qmlcomponents/ImButton.qml qmlcomponents/ImButtonRed.qml qmlcomponents/ImCheckBox.qml + qmlcomponents/ImPopup.qml qmlcomponents/ImRadioButton.qml icons/cat_digital_signage.png diff --git a/src/qmlcomponents/ImPopup.qml b/src/qmlcomponents/ImPopup.qml new file mode 100644 index 00000000..fd3e75f3 --- /dev/null +++ b/src/qmlcomponents/ImPopup.qml @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Material 2.2 + +Popup { + id: msgpopup + x: (parent.width-width)/2 + y: (parent.height-height)/2 + width: 550 + padding: 0 + closePolicy: Popup.CloseOnEscape + modal: true + + default property alias content: contents.data + + property alias title: msgpopupheader.text + + signal yes() + signal no() + + // background of title + Rectangle { + id: msgpopup_title_background + color: "#f5f5f5" + anchors.left: parent.left + anchors.top: parent.top + height: 35 + width: parent.width + + Text { + id: msgpopupheader + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.topMargin: 10 + font.family: roboto.name + font.bold: true + } + + Text { + text: "X" + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: roboto.name + font.bold: true + + MouseArea { + anchors.fill: parent + // KDAB: This was not present in MessagePopup + cursorShape: Qt.PointingHandCursor + onClicked: { + msgpopup.close() + } + } + } + } + // line under title + Rectangle { + id: msgpopup_title_separator + color: "#afafaf" + width: parent.width + anchors.top: msgpopup_title_background.bottom + height: 1 + } + + ColumnLayout { + id: contents + spacing: 20 + anchors.top: msgpopup_title_separator.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + // Derived components inject their childrens here. + } +} From d04dbc40df04df93cb425d081a14bdb3b58cbcc5 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Mon, 3 Feb 2025 16:57:28 +0000 Subject: [PATCH 03/47] UseSavedSettingsPopup: Fix warnings about unqualified accesses Makes qmllint happy and allows us to make such warnings fatal soon, to catch real bugs. --- src/UseSavedSettingsPopup.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UseSavedSettingsPopup.qml b/src/UseSavedSettingsPopup.qml index b6d8c6dc..55944fe9 100644 --- a/src/UseSavedSettingsPopup.qml +++ b/src/UseSavedSettingsPopup.qml @@ -62,7 +62,7 @@ ImPopup { root.close() root.noClearSettings() } - enabled: hasSavedSettings + enabled: root.hasSavedSettings } ImButtonRed { @@ -72,7 +72,7 @@ ImPopup { root.close() root.yes() } - enabled: hasSavedSettings + enabled: root.hasSavedSettings } ImButtonRed { From dbb8d891fc6eadc4f082247bf600cde0030c9676 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Tue, 4 Feb 2025 18:26:24 +0000 Subject: [PATCH 04/47] Split OptionsPopup.qml into multiple files One file per tab. Besides reducing the size of OptionsPopup.qml it's also nice that each tab has an "interface" with public properties while hiding the private impl. For OptionsMiscTab.qml I've improved semantics by only exposing a bool telemetryEnabled instead of the whole check-box, which was very verbose in the caller site. In a followup the 3 tabs will inherit from a base component, so we can share the scrolling behaviour properties --- src/OptionsGeneralTab.qml | 301 +++++++++++++++ src/OptionsMiscTab.qml | 44 +++ src/OptionsPopup.qml | 763 ++++++++----------------------------- src/OptionsServicesTab.qml | 168 ++++++++ src/qml.qrc | 3 + 5 files changed, 680 insertions(+), 599 deletions(-) create mode 100644 src/OptionsGeneralTab.qml create mode 100644 src/OptionsMiscTab.qml create mode 100644 src/OptionsServicesTab.qml diff --git a/src/OptionsGeneralTab.qml b/src/OptionsGeneralTab.qml new file mode 100644 index 00000000..d4fd3221 --- /dev/null +++ b/src/OptionsGeneralTab.qml @@ -0,0 +1,301 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.15 +import QtQuick.Controls.Material 2.2 +import QtQuick.Window 2.15 +import "qmlcomponents" + +ScrollView { + id: root + + property alias fieldHostname: fieldHostname + property alias fieldUserName: fieldUserName + property alias fieldTimezone: fieldTimezone + property alias fieldWifiCountry: fieldWifiCountry + property alias fieldKeyboardLayout: fieldKeyboardLayout + property alias fieldWifiPassword: fieldWifiPassword + property alias fieldWifiSSID: fieldWifiSSID + property alias chkShowPassword: chkShowPassword + property alias chkWifi: chkWifi + property alias chkWifiSSIDHidden: chkWifiSSIDHidden + property alias chkHostname: chkHostname + property alias chkLocale: chkLocale + property alias chkSetUser: chkSetUser + property alias fieldUserPassword: fieldUserPassword + + required property bool sshEnabled + required property bool passwordAuthenticationEnabled + + font.family: roboto.name + + Layout.fillWidth: true + Layout.fillHeight: true + + property double scrollPosition + + // clip: true + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ScrollBar.vertical.position: scrollPosition + scrollPosition: scrollPosition + ColumnLayout { + RowLayout { + ImCheckBox { + id: chkHostname + text: qsTr("Set hostname:") + onCheckedChanged: { + if (checked) { + fieldHostname.forceActiveFocus() + } + } + } + // Spacer item + Item { + Layout.fillWidth: true + } + TextField { + id: fieldHostname + enabled: chkHostname.checked + text: "raspberrypi" + selectByMouse: true + maximumLength: 253 + validator: RegularExpressionValidator { regularExpression: /[0-9A-Za-z][0-9A-Za-z-]{0,62}/ } + Layout.minimumWidth: 200 + } + Text { + text : ".local" + color: chkHostname.checked ? "black" : "grey" + } + } + + ImCheckBox { + id: chkSetUser + text: qsTr("Set username and password") + onCheckedChanged: { + if (!checked && root.sshEnabled && root.passwordAuthenticationEnabled) { + checked = true; + } + if (checked && !fieldUserPassword.length) { + fieldUserPassword.forceActiveFocus() + } + } + } + + RowLayout { + Text { + text: qsTr("Username:") + color: parent.enabled ? (fieldUserName.indicateError ? "red" : "black") : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + TextField { + id: fieldUserName + enabled: chkSetUser.checked + text: "pi" + Layout.minimumWidth: 200 + selectByMouse: true + property bool indicateError: false + maximumLength: 31 + validator: RegularExpressionValidator { regularExpression: /^[a-z][a-z0-9-]{0,30}$/ } + + onTextEdited: { + indicateError = false + } + } + } + RowLayout { + Text { + text: qsTr("Password:") + color: parent.enabled ? (fieldUserPassword.indicateError ? "red" : "black") : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + TextField { + id: fieldUserPassword + enabled: chkSetUser.checked + echoMode: TextInput.Password + passwordMaskDelay: 2000 //ms + Layout.minimumWidth: 200 + selectByMouse: true + property bool alreadyCrypted: false + property bool indicateError: false + + Keys.onPressed: (event)=> { + if (alreadyCrypted) { + /* User is trying to edit saved + (crypted) password, clear field */ + alreadyCrypted = false + clear() + } + + // Do not mark the event as accepted, so that it may + // propagate down to the underlying TextField. + event.accepted = false + } + + onTextEdited: { + if (indicateError) { + indicateError = false + } + } + } + } + + ImCheckBox { + id: chkWifi + text: qsTr("Configure wireless LAN") + onCheckedChanged: { + if (checked) { + if (!fieldWifiSSID.length) { + fieldWifiSSID.forceActiveFocus() + } else if (!fieldWifiPassword.length) { + fieldWifiPassword.forceActiveFocus() + } + } + } + } + + RowLayout { + Text { + text: qsTr("SSID:") + color: chkWifi.checked ? (fieldWifiSSID.indicateError ? "red" : "black") : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + TextField { + id: fieldWifiSSID + // placeholderText: qsTr("SSID") + enabled: chkWifi.checked + Layout.minimumWidth: 200 + selectByMouse: true + property bool indicateError: false + onTextEdited: { + indicateError = false + } + } + } + RowLayout { + Text { + text: qsTr("Password:") + color: chkWifi.checked ? (fieldWifiPassword.indicateError ? "red" : "black") : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + TextField { + id: fieldWifiPassword + enabled: chkWifi.checked + Layout.minimumWidth: 200 + selectByMouse: true + echoMode: chkShowPassword.checked ? TextInput.Normal : TextInput.Password + property bool indicateError: false + onTextEdited: { + indicateError = false + } + } + } + + RowLayout { + // Spacer item + Item { + Layout.fillWidth: true + } + ImCheckBox { + id: chkShowPassword + enabled: chkWifi.checked + text: qsTr("Show password") + checked: true + } + // Spacer item + Item { + Layout.fillWidth: true + } + ImCheckBox { + id: chkWifiSSIDHidden + enabled: chkWifi.checked + Layout.columnSpan: 2 + text: qsTr("Hidden SSID") + checked: false + } + // Spacer item + Item { + Layout.fillWidth: true + } + } + + RowLayout { + Text { + text: qsTr("Wireless LAN country:") + color: chkWifi.checked ? "black" : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + ComboBox { + id: fieldWifiCountry + selectTextByMouse: true + enabled: chkWifi.checked + editable: true + } + } + + ImCheckBox { + id: chkLocale + text: qsTr("Set locale settings") + } + RowLayout { + Text { + text: qsTr("Time zone:") + color: chkLocale.checked ? "black" : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + ComboBox { + enabled: chkLocale.checked + selectTextByMouse: true + id: fieldTimezone + editable: true + Layout.minimumWidth: 200 + } + } + + RowLayout { + Text { + text: qsTr("Keyboard layout:") + color: chkLocale.checked ? "black" : "grey" + Layout.leftMargin: 40 + } + // Spacer item + Item { + Layout.fillWidth: true + } + ComboBox { + enabled: chkLocale.checked + selectTextByMouse: true + id: fieldKeyboardLayout + editable: true + Layout.minimumWidth: 200 + } + } + } +} diff --git a/src/OptionsMiscTab.qml b/src/OptionsMiscTab.qml new file mode 100644 index 00000000..445f092a --- /dev/null +++ b/src/OptionsMiscTab.qml @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Window 2.15 +import "qmlcomponents" + +ScrollView { + id: root + + property alias beepEnabled: chkBeep.checked + property alias telemetryEnabled: chkTelemtry.checked + property alias ejectEnabled: chkEject.checked + + font.family: roboto.name + + Layout.fillWidth: true + Layout.fillHeight: true + + property double scrollPosition + + // clip: true + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ScrollBar.vertical.position: scrollPosition + scrollPosition: scrollPosition + + ColumnLayout { + ImCheckBox { + id: chkBeep + text: qsTr("Play sound when finished") + } + ImCheckBox { + id: chkEject + text: qsTr("Eject media when finished") + } + ImCheckBox { + id: chkTelemtry + text: qsTr("Enable telemetry") + } + } +} diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index 0ba94b0a..0f06fe6a 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -8,7 +8,6 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 -import QtQml.Models import "qmlcomponents" Window { @@ -58,8 +57,8 @@ Window { TabButton { text: qsTr("General") onClicked: { - if (chkSetUser.checked && !fieldUserPassword.length) { - fieldUserPassword.forceActiveFocus() + if (generalTab.chkSetUser.checked && !generalTab.fieldUserPassword.length) { + generalTab.fieldUserPassword.forceActiveFocus() } generalTab.scrollPosition = 0 } @@ -86,455 +85,21 @@ Window { Layout.fillHeight: true currentIndex: bar.currentIndex - ScrollView { + OptionsGeneralTab { id: generalTab - font.family: roboto.name - - Layout.fillWidth: true - Layout.fillHeight: true - - property double scrollPosition - - // clip: true - ScrollBar.vertical.policy: ScrollBar.AsNeeded - ScrollBar.vertical.position: scrollPosition - scrollPosition: scrollPosition - ColumnLayout { - // General tab - - RowLayout { - ImCheckBox { - id: chkHostname - text: qsTr("Set hostname:") - onCheckedChanged: { - if (checked) { - fieldHostname.forceActiveFocus() - } - } - } - // Spacer item - Item { - Layout.fillWidth: true - } - TextField { - id: fieldHostname - enabled: chkHostname.checked - text: "raspberrypi" - selectByMouse: true - maximumLength: 253 - validator: RegularExpressionValidator { regularExpression: /[0-9A-Za-z][0-9A-Za-z-]{0,62}/ } - Layout.minimumWidth: 200 - } - Text { - text : ".local" - color: chkHostname.checked ? "black" : "grey" - } - } - - ImCheckBox { - id: chkSetUser - text: qsTr("Set username and password") - onCheckedChanged: { - if (!checked && chkSSH.checked && radioPasswordAuthentication.checked) { - checked = true; - } - if (checked && !fieldUserPassword.length) { - fieldUserPassword.forceActiveFocus() - } - } - } - - RowLayout { - Text { - text: qsTr("Username:") - color: parent.enabled ? (fieldUserName.indicateError ? "red" : "black") : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - TextField { - id: fieldUserName - enabled: chkSetUser.checked - text: "pi" - Layout.minimumWidth: 200 - selectByMouse: true - property bool indicateError: false - maximumLength: 31 - validator: RegularExpressionValidator { regularExpression: /^[a-z][a-z0-9-]{0,30}$/ } - - onTextEdited: { - indicateError = false - } - } - } - RowLayout { - Text { - text: qsTr("Password:") - color: parent.enabled ? (fieldUserPassword.indicateError ? "red" : "black") : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - TextField { - id: fieldUserPassword - enabled: chkSetUser.checked - echoMode: TextInput.Password - passwordMaskDelay: 2000 //ms - Layout.minimumWidth: 200 - selectByMouse: true - property bool alreadyCrypted: false - property bool indicateError: false - - Keys.onPressed: (event)=> { - if (alreadyCrypted) { - /* User is trying to edit saved - (crypted) password, clear field */ - alreadyCrypted = false - clear() - } - - // Do not mark the event as accepted, so that it may - // propagate down to the underlying TextField. - event.accepted = false - } - - onTextEdited: { - if (indicateError) { - indicateError = false - } - } - } - } - - ImCheckBox { - id: chkWifi - text: qsTr("Configure wireless LAN") - onCheckedChanged: { - if (checked) { - if (!fieldWifiSSID.length) { - fieldWifiSSID.forceActiveFocus() - } else if (!fieldWifiPassword.length) { - fieldWifiPassword.forceActiveFocus() - } - } - } - } - - RowLayout { - Text { - text: qsTr("SSID:") - color: chkWifi.checked ? (fieldWifiSSID.indicateError ? "red" : "black") : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - TextField { - id: fieldWifiSSID - // placeholderText: qsTr("SSID") - enabled: chkWifi.checked - Layout.minimumWidth: 200 - selectByMouse: true - property bool indicateError: false - onTextEdited: { - indicateError = false - } - } - } - RowLayout { - Text { - text: qsTr("Password:") - color: chkWifi.checked ? (fieldWifiPassword.indicateError ? "red" : "black") : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - TextField { - id: fieldWifiPassword - enabled: chkWifi.checked - Layout.minimumWidth: 200 - selectByMouse: true - echoMode: chkShowPassword.checked ? TextInput.Normal : TextInput.Password - property bool indicateError: false - onTextEdited: { - indicateError = false - } - } - } - - RowLayout { - // Spacer item - Item { - Layout.fillWidth: true - } - ImCheckBox { - id: chkShowPassword - enabled: chkWifi.checked - text: qsTr("Show password") - checked: true - } - // Spacer item - Item { - Layout.fillWidth: true - } - ImCheckBox { - id: chkWifiSSIDHidden - enabled: chkWifi.checked - Layout.columnSpan: 2 - text: qsTr("Hidden SSID") - checked: false - } - // Spacer item - Item { - Layout.fillWidth: true - } - } - - RowLayout { - Text { - text: qsTr("Wireless LAN country:") - color: chkWifi.checked ? "black" : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - ComboBox { - id: fieldWifiCountry - selectTextByMouse: true - enabled: chkWifi.checked - editable: true - } - } - - ImCheckBox { - id: chkLocale - text: qsTr("Set locale settings") - } - RowLayout { - Text { - text: qsTr("Time zone:") - color: chkLocale.checked ? "black" : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - ComboBox { - enabled: chkLocale.checked - selectTextByMouse: true - id: fieldTimezone - editable: true - Layout.minimumWidth: 200 - } - } - - RowLayout { - Text { - text: qsTr("Keyboard layout:") - color: chkLocale.checked ? "black" : "grey" - Layout.leftMargin: 40 - } - // Spacer item - Item { - Layout.fillWidth: true - } - ComboBox { - enabled: chkLocale.checked - selectTextByMouse: true - id: fieldKeyboardLayout - editable: true - Layout.minimumWidth: 200 - } - } - } + sshEnabled: remoteAccessTab.chkSSH.enabled + passwordAuthenticationEnabled: remoteAccessTab.radioPasswordAuthentication.enabled } - ScrollView { + OptionsServicesTab { id: remoteAccessTab - font.family: roboto.name - - Layout.fillWidth: true - Layout.fillHeight: true - - property double scrollPosition - - // clip: true - ScrollBar.vertical.policy: ScrollBar.AsNeeded - ScrollBar.vertical.position: scrollPosition - scrollPosition: scrollPosition - ColumnLayout { - // Remote access tab - Layout.fillWidth: true - - ImCheckBox { - id: chkSSH - text: qsTr("Enable SSH") - onCheckedChanged: { - if (checked) { - if (!radioPasswordAuthentication.checked && !radioPubKeyAuthentication.checked) { - radioPasswordAuthentication.checked = true - } - if (radioPasswordAuthentication.checked) { - chkSetUser.checked = true - } - } - } - } - - ImRadioButton { - id: radioPasswordAuthentication - enabled: chkSSH.checked - text: qsTr("Use password authentication") - onCheckedChanged: { - if (checked) { - chkSetUser.checked = true - //fieldUserPassword.forceActiveFocus() - } - } - } - ImRadioButton { - id: radioPubKeyAuthentication - enabled: chkSSH.checked - text: qsTr("Allow public-key authentication only") - onCheckedChanged: { - if (checked) { - if (chkSetUser.checked && fieldUserName.text == "pi" && fieldUserPassword.text.length == 0) { - chkSetUser.checked = false - } - } - } - } - - Text { - text: qsTr("Set authorized_keys for '%1':").arg(fieldUserName.text) - color: radioPubKeyAuthentication.checked ? "black" : "grey" - // textFormat: Text.PlainText - Layout.leftMargin: 40 - } - Item { - id: publicKeyListViewContainer - Layout.leftMargin: 40 - Layout.rightMargin: 5 - Layout.minimumHeight: 50 - Layout.fillHeight: true - Layout.preferredWidth: popup.width - (20 + Layout.leftMargin + Layout.rightMargin) - - ListView { - id: publicKeyList - model: ListModel { - id: publicKeyModel - - } - boundsBehavior: Flickable.StopAtBounds - anchors.fill: parent - spacing: 12 - clip: true - - delegate: RowLayout { - id: publicKeyItem - property int publicKeyModelIndex: index - height: 50 - - TextField { - id: contentField - enabled: radioPubKeyAuthentication.checked - validator: RegularExpressionValidator { regularExpression: /^ssh-(ed25519|rsa|dss|ecdsa) AAAA(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})( [A-Za-z0-9-\\\/]+@[A-Za-z0-9-\\\/]+)?/ } - text: model.publicKeyField - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - implicitWidth: publicKeyList.width - (removePublicKeyItem.width + 20) - - onEditingFinished: { - publicKeyModel.set(publicKeyModelIndex, {publicKeyField: contentField.text}) - } - } - ImButton { - id: removePublicKeyItem - text: qsTr("Delete Key") - enabled: radioPubKeyAuthentication.checked - Layout.minimumWidth: 100 - Layout.preferredWidth: 100 - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - - onClicked: { - if (publicKeyModelIndex != -1) { - publicKeyModel.remove(publicKeyModelIndex) - publicKeyListViewContainer.implicitHeight -= 50 + publicKeyList.spacing - } - } - } - } - } - } - - RowLayout { - ImButton { - text: qsTr("RUN SSH-KEYGEN") - Layout.leftMargin: 40 - enabled: imageWriter.hasSshKeyGen() && !imageWriter.hasPubKey() - onClicked: { - enabled = false - imageWriter.generatePubKey() - publicKeyModel.append({publicKeyField: imageWriter.getDefaultPubKey()}) - } - } - ImButton { - text: qsTr("Add SSH Key") - Layout.leftMargin: 40 - enabled: radioPubKeyAuthentication.checked - onClicked: { - publicKeyModel.append({publicKeyField: ""}) - if (publicKeyListViewContainer.implicitHeight) { - publicKeyListViewContainer.implicitHeight += 50 + publicKeyList.spacing - } else { - publicKeyListViewContainer.implicitHeight += 100 + (publicKeyList.spacing) - } - } - } - } - } + chkSetUser: generalTab.chkSetUser + fieldUserName: generalTab.fieldUserName + fieldUserPassword: generalTab.fieldUserPassword } - ScrollView { + OptionsMiscTab { id: optionsTab - font.family: roboto.name - - Layout.fillWidth: true - Layout.fillHeight: true - - property double scrollPosition - - // clip: true - ScrollBar.vertical.policy: ScrollBar.AsNeeded - ScrollBar.vertical.position: scrollPosition - scrollPosition: scrollPosition - - ColumnLayout { - // Options tab - - ImCheckBox { - id: chkBeep - text: qsTr("Play sound when finished") - } - ImCheckBox { - id: chkEject - text: qsTr("Eject media when finished") - } - ImCheckBox { - id: chkTelemtry - text: qsTr("Enable telemetry") - } - } } } @@ -549,39 +114,39 @@ Window { ImButtonRed { text: qsTr("SAVE") onClicked: { - if (chkSetUser.checked && fieldUserPassword.text.length == 0) + if (generalTab.chkSetUser.checked && generalTab.fieldUserPassword.text.length == 0) { - fieldUserPassword.indicateError = true - fieldUserPassword.forceActiveFocus() + generalTab.fieldUserPassword.indicateError = true + generalTab.fieldUserPassword.forceActiveFocus() bar.currentIndex = 0 return } - if (chkSetUser.checked && fieldUserName.text.length == 0) + if (generalTab.chkSetUser.checked && generalTab.fieldUserName.text.length == 0) { - fieldUserName.indicateError = true - fieldUserName.forceActiveFocus() + generalTab.fieldUserName.indicateError = true + generalTab.fieldUserName.forceActiveFocus() bar.currentIndex = 0 return } - if (chkWifi.checked) + if (generalTab.chkWifi.checked) { // Valid Wi-Fi PSKs are: // - 0 characters (indicating an open network) // - 8-63 characters (passphrase) // - 64 characters (hashed passphrase, as hex) - if (fieldWifiPassword.text.length > 0 && - (fieldWifiPassword.text.length < 8 || fieldWifiPassword.text.length > 64)) + if (generalTab.fieldWifiPassword.text.length > 0 && + (generalTab.fieldWifiPassword.text.length < 8 || generalTab.fieldWifiPassword.text.length > 64)) { - fieldWifiPassword.indicateError = true - fieldWifiPassword.forceActiveFocus() + generalTab.fieldWifiPassword.indicateError = true + generalTab.fieldWifiPassword.forceActiveFocus() } - if (fieldWifiSSID.text.length == 0) + if (generalTab.fieldWifiSSID.text.length == 0) { - fieldWifiSSID.indicateError = true - fieldWifiSSID.forceActiveFocus() + generalTab.fieldWifiSSID.indicateError = true + generalTab.fieldWifiSSID.forceActiveFocus() } - if (fieldWifiSSID.indicateError || fieldWifiPassword.indicateError) + if (generalTab.fieldWifiSSID.indicateError || generalTab.fieldWifiPassword.indicateError) { bar.currentIndex = 0 return @@ -601,30 +166,30 @@ Window { } function initialize() { - chkBeep.checked = imageWriter.getBoolSetting("beep") - chkTelemtry.checked = imageWriter.getBoolSetting("telemetry") - chkEject.checked = imageWriter.getBoolSetting("eject") + optionsTab.beepEnabled = imageWriter.getBoolSetting("beep") + optionsTab.telemetryEnabled = imageWriter.getBoolSetting("telemetry") + optionsTab.ejectEnabled = imageWriter.getBoolSetting("eject") var settings = imageWriter.getSavedCustomizationSettings() - fieldTimezone.model = imageWriter.getTimezoneList() - fieldWifiCountry.model = imageWriter.getCountryList() - fieldKeyboardLayout.model = imageWriter.getKeymapLayoutList() + generalTab.fieldTimezone.model = imageWriter.getTimezoneList() + generalTab.fieldWifiCountry.model = imageWriter.getCountryList() + generalTab.fieldKeyboardLayout.model = imageWriter.getKeymapLayoutList() if (Object.keys(settings).length) { hasSavedSettings = true } if ('hostname' in settings) { fieldHostname.text = settings.hostname - chkHostname.checked = true + generalTab.chkHostname.checked = true } if ('sshUserPassword' in settings) { - fieldUserPassword.text = settings.sshUserPassword - fieldUserPassword.alreadyCrypted = true - chkSetUser.checked = true + generalTab.fieldUserPassword.text = settings.sshUserPassword + generalTab.fieldUserPassword.alreadyCrypted = true + generalTab.chkSetUser.checked = true /* Older imager versions did not have a sshEnabled setting. Assume it is true if it does not exists and sshUserPassword is set */ if (!('sshEnabled' in settings) || settings.sshEnabled === "true" || settings.sshEnabled === true) { - chkSSH.checked = true - radioPasswordAuthentication.checked = true + remoteAccessTab.chkSSH.checked = true + remoteAccessTab.radioPasswordAuthentication.checked = true } } if ('sshAuthorizedKeys' in settings) { @@ -633,57 +198,57 @@ Window { for (const publicKey of possiblePublicKeys) { var pkitem = publicKey.trim() if (pkitem) { - publicKeyModel.append({publicKeyField: pkitem}) + remoteAccessTab.publicKeyModel.append({publicKeyField: pkitem}) } } - radioPubKeyAuthentication.checked = true - chkSSH.checked = true + remoteAccessTab.radioPubKeyAuthentication.checked = true + remoteAccessTab.chkSSH.checked = true } // Attempt to insert the default public key, but avoid clashes. { var insertDefaultKey = true var defaultKey = imageWriter.getDefaultPubKey() - for (var i = 0; i/etc/hostname") - addFirstRun(" sed -i \"s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\\t"+fieldHostname.text+"/g\" /etc/hosts") + addFirstRun(" echo "+generalTab.fieldHostname.text+" >/etc/hostname") + addFirstRun(" sed -i \"s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\\t"+generalTab.fieldHostname.text+"/g\" /etc/hosts") addFirstRun("fi") - addCloudInit("hostname: "+fieldHostname.text) + addCloudInit("hostname: "+generalTab.fieldHostname.text) addCloudInit("manage_etc_hosts: true") addCloudInit("packages:") addCloudInit("- avahi-daemon") @@ -808,29 +373,29 @@ Window { addCloudInit("") } - if (chkSSH.checked || chkSetUser.checked) { + if (remoteAccessTab.chkSSH.checked || generalTab.chkSetUser.checked) { // First user may not be called 'pi' on all distributions, so look username up addFirstRun("FIRSTUSER=`getent passwd 1000 | cut -d: -f1`"); addFirstRun("FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`") addCloudInit("users:") - addCloudInit("- name: "+fieldUserName.text) + addCloudInit("- name: "+generalTab.fieldUserName.text) addCloudInit(" groups: users,adm,dialout,audio,netdev,video,plugdev,cdrom,games,input,gpio,spi,i2c,render,sudo") addCloudInit(" shell: /bin/bash") var cryptedPassword; - if (chkSetUser.checked) { - cryptedPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text) + if (generalTab.chkSetUser.checked) { + cryptedPassword = generalTab.fieldUserPassword.alreadyCrypted ? generalTab.fieldUserPassword.text : imageWriter.crypt(generalTab.fieldUserPassword.text) addCloudInit(" lock_passwd: false") addCloudInit(" passwd: "+cryptedPassword) } - if (chkSSH.checked && radioPubKeyAuthentication.checked) { + if (remoteAccessTab.chkSSH.checked && remoteAccessTab.radioPubKeyAuthentication.checked) { var pubkeySpaceSep = '' var pubKeyNewlineSep = '' - for (var j=0; j= 8 && - fieldWifiPassword.text.length < 64 - var cryptedPsk = isPassphrase ? imageWriter.pbkdf2(fieldWifiPassword.text, fieldWifiSSID.text) - : fieldWifiPassword.text + const isPassphrase = generalTab.fieldWifiPassword.text.length >= 8 && + generalTab.fieldWifiPassword.text.length < 64 + var cryptedPsk = isPassphrase ? imageWriter.pbkdf2(generalTab.fieldWifiPassword.text, generalTab.fieldWifiSSID.text) + : generalTab.fieldWifiPassword.text wpaconfig += "\tpsk="+cryptedPsk+"\n" wpaconfig += "}\n" addFirstRun("if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then") addFirstRun(" /usr/lib/raspberrypi-sys-mods/imager_custom set_wlan " - +(chkWifiSSIDHidden.checked ? " -h " : "") - +escapeshellarg(fieldWifiSSID.text)+" "+escapeshellarg(cryptedPsk)+" "+escapeshellarg(fieldWifiCountry.editText)) + +(generalTab.chkWifiSSIDHidden.checked ? " -h " : "") + +escapeshellarg(generalTab.fieldWifiSSID.text)+" "+escapeshellarg(cryptedPsk)+" "+escapeshellarg(generalTab.fieldWifiCountry.editText)) addFirstRun("else") addFirstRun("cat >/etc/wpa_supplicant/wpa_supplicant.conf <<'WPAEOF'") addFirstRun(wpaconfig) @@ -934,26 +499,26 @@ Window { cloudinitnetwork += " dhcp4: true\n" cloudinitnetwork += " optional: true\n" cloudinitnetwork += " access-points:\n" - cloudinitnetwork += " \""+fieldWifiSSID.text+"\":\n" + cloudinitnetwork += " \""+generalTab.fieldWifiSSID.text+"\":\n" cloudinitnetwork += " password: \""+cryptedPsk+"\"\n" - if (chkWifiSSIDHidden.checked) { + if (generalTab.chkWifiSSIDHidden.checked) { cloudinitnetwork += " hidden: true\n" } - addCmdline("cfg80211.ieee80211_regdom="+fieldWifiCountry.editText) + addCmdline("cfg80211.ieee80211_regdom="+generalTab.fieldWifiCountry.editText) } - if (chkLocale.checked) { + if (generalTab.chkLocale.checked) { var kbdconfig = "XKBMODEL=\"pc105\"\n" - kbdconfig += "XKBLAYOUT=\""+fieldKeyboardLayout.editText+"\"\n" + kbdconfig += "XKBLAYOUT=\""+generalTab.fieldKeyboardLayout.editText+"\"\n" kbdconfig += "XKBVARIANT=\"\"\n" kbdconfig += "XKBOPTIONS=\"\"\n" addFirstRun("if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then") - addFirstRun(" /usr/lib/raspberrypi-sys-mods/imager_custom set_keymap "+escapeshellarg(fieldKeyboardLayout.editText)) - addFirstRun(" /usr/lib/raspberrypi-sys-mods/imager_custom set_timezone "+escapeshellarg(fieldTimezone.editText)) + addFirstRun(" /usr/lib/raspberrypi-sys-mods/imager_custom set_keymap "+escapeshellarg(generalTab.fieldKeyboardLayout.editText)) + addFirstRun(" /usr/lib/raspberrypi-sys-mods/imager_custom set_timezone "+escapeshellarg(generalTab.fieldTimezone.editText)) addFirstRun("else") addFirstRun(" rm -f /etc/localtime") - addFirstRun(" echo \""+fieldTimezone.editText+"\" >/etc/timezone") + addFirstRun(" echo \""+generalTab.fieldTimezone.editText+"\" >/etc/timezone") addFirstRun(" dpkg-reconfigure -f noninteractive tzdata") addFirstRun("cat >/etc/default/keyboard <<'KBEOF'") addFirstRun(kbdconfig) @@ -961,10 +526,10 @@ Window { addFirstRun(" dpkg-reconfigure -f noninteractive keyboard-configuration") addFirstRun("fi") - addCloudInit("timezone: "+fieldTimezone.editText) + addCloudInit("timezone: "+generalTab.fieldTimezone.editText) addCloudInit("keyboard:") addCloudInit(" model: pc105") - addCloudInit(" layout: \"" + fieldKeyboardLayout.editText + "\"") + addCloudInit(" layout: \"" + generalTab.fieldKeyboardLayout.editText + "\"") } if (firstrun.length) { @@ -992,19 +557,19 @@ Window { function saveSettings() { var settings = { }; - if (chkHostname.checked && fieldHostname.length) { - settings.hostname = fieldHostname.text + if (generalTab.chkHostname.checked && generalTab.fieldHostname.length) { + settings.hostname = generalTab.fieldHostname.text } - if (chkSetUser.checked) { - settings.sshUserName = fieldUserName.text - settings.sshUserPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text) + if (generalTab.chkSetUser.checked) { + settings.sshUserName = generalTab.fieldUserName.text + settings.sshUserPassword = generalTab.fieldUserPassword.alreadyCrypted ? generalTab.fieldUserPassword.text : imageWriter.crypt(generalTab.fieldUserPassword.text) } - settings.sshEnabled = chkSSH.checked - if (chkSSH.checked && radioPubKeyAuthentication.checked) { + settings.sshEnabled = remoteAccessTab.chkSSH.checked + if (remoteAccessTab.chkSSH.checked && remoteAccessTab.radioPubKeyAuthentication.checked) { var publicKeysSerialised = "" - for (var j=0; j= 8 && - fieldWifiPassword.text.length < 64 - var cryptedPsk = isPassphrase ? imageWriter.pbkdf2(fieldWifiPassword.text, fieldWifiSSID.text) - : fieldWifiPassword.text + const isPassphrase = generalTab.fieldWifiPassword.text.length >= 8 && + generalTab.fieldWifiPassword.text.length < 64 + var cryptedPsk = isPassphrase ? imageWriter.pbkdf2(generalTab.fieldWifiPassword.text, generalTab.fieldWifiSSID.text) + : generalTab.fieldWifiPassword.text settings.wifiPassword = cryptedPsk } - if (chkLocale.checked) { - settings.timezone = fieldTimezone.editText - settings.keyboardLayout = fieldKeyboardLayout.editText + if (generalTab.chkLocale.checked) { + settings.timezone = generalTab.fieldTimezone.editText + settings.keyboardLayout = generalTab.fieldKeyboardLayout.editText } - imageWriter.setSetting("beep", chkBeep.checked) - imageWriter.setSetting("eject", chkEject.checked) - imageWriter.setSetting("telemetry", chkTelemtry.checked) + imageWriter.setSetting("beep", optionsTab.beepEnabled) + imageWriter.setSetting("eject", optionsTab.ejectEnabled) + imageWriter.setSetting("telemetry", optionsTab.telemetryEnabled) - if (chkHostname.checked || chkSetUser.checked || chkSSH.checked || chkWifi.checked || chkLocale.checked) { + if (generalTab.chkHostname.checked || generalTab.chkSetUser.checked || remoteAccessTab.chkSSH.checked || generalTab.chkWifi.checked || generalTab.chkLocale.checked) { /* OS customization to be applied. */ hasSavedSettings = true saveSettingsSignal(settings) @@ -1044,43 +609,43 @@ Window { function clearCustomizationFields() { /* Bind copies of the lists */ - fieldTimezone.model = imageWriter.getTimezoneList() - fieldKeyboardLayout.model = imageWriter.getKeymapLayoutList() - fieldWifiCountry.model = imageWriter.getCountryList() + generalTab.fieldTimezone.model = imageWriter.getTimezoneList() + generalTab.fieldKeyboardLayout.model = imageWriter.getKeymapLayoutList() + generalTab.fieldWifiCountry.model = imageWriter.getCountryList() fieldHostname.text = "raspberrypi" - fieldUserName.text = imageWriter.getCurrentUser() - fieldUserPassword.clear() - radioPubKeyAuthentication.checked = false - radioPasswordAuthentication.checked = false - publicKeyModel.clear() + generalTab.fieldUserName.text = imageWriter.getCurrentUser() + generalTab.fieldUserPassword.clear() + remoteAccessTab.radioPubKeyAuthentication.checked = false + remoteAccessTab.radioPasswordAuthentication.checked = false + remoteAccessTab.publicKeyModel.clear() /* Timezone Settings*/ - fieldTimezone.currentIndex = fieldTimezone.find(imageWriter.getTimezone()) + generalTab.fieldTimezone.currentIndex = generalTab.fieldTimezone.find(imageWriter.getTimezone()) /* Lacking an easy cross-platform to fetch keyboard layout from host system, just default to "gb" for people in UK time zone for now, and "us" for everyone else */ if (imageWriter.getTimezone() === "Europe/London") { - fieldKeyboardLayout.currentIndex = fieldKeyboardLayout.find("gb") + generalTab.fieldKeyboardLayout.currentIndex = generalTab.fieldKeyboardLayout.find("gb") } else { - fieldKeyboardLayout.currentIndex = fieldKeyboardLayout.find("us") + generalTab.fieldKeyboardLayout.currentIndex = generalTab.fieldKeyboardLayout.find("us") } - chkSetUser.checked = false - chkSSH.checked = false - chkLocale.checked = false - chkWifiSSIDHidden.checked = false - chkHostname.checked = false + generalTab.chkSetUser.checked = false + remoteAccessTab.chkSSH.checked = false + generalTab.chkLocale.checked = false + generalTab.chkWifiSSIDHidden.checked = false + generalTab.chkHostname.checked = false /* WiFi Settings */ - fieldWifiSSID.text = imageWriter.getSSID() - if (fieldWifiSSID.text.length) { - fieldWifiPassword.text = imageWriter.getPSK() - if (fieldWifiPassword.text.length) { - chkShowPassword.checked = false + generalTab.fieldWifiSSID.text = imageWriter.getSSID() + if (generalTab.fieldWifiSSID.text.length) { + generalTab.fieldWifiPassword.text = imageWriter.getPSK() + if (generalTab.fieldWifiPassword.text.length) { + generalTab.chkShowPassword.checked = false if (Qt.platform.os == "osx") { /* User indicated wifi must be prefilled */ - chkWifi.checked = true + generalTab.chkWifi.checked = true } } } diff --git a/src/OptionsServicesTab.qml b/src/OptionsServicesTab.qml new file mode 100644 index 00000000..322b3075 --- /dev/null +++ b/src/OptionsServicesTab.qml @@ -0,0 +1,168 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.15 +import QtQuick.Controls.Material 2.2 +import QtQuick.Window 2.15 +import "qmlcomponents" + +ScrollView { + id: root + + property alias publicKeyModel: publicKeyModel + + property alias chkSSH: chkSSH + property alias radioPasswordAuthentication: radioPasswordAuthentication + property alias radioPubKeyAuthentication: radioPubKeyAuthentication + + property double scrollPosition + + Layout.fillWidth: true + Layout.fillHeight: true + font.family: roboto.name + + + + // clip: true + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ScrollBar.vertical.position: scrollPosition + scrollPosition: scrollPosition + required property ImCheckBox chkSetUser + required property TextField fieldUserName + required property TextField fieldUserPassword + + ColumnLayout { + Layout.fillWidth: true + + ImCheckBox { + id: chkSSH + text: qsTr("Enable SSH") + onCheckedChanged: { + if (checked) { + if (!radioPasswordAuthentication.checked && !radioPubKeyAuthentication.checked) { + radioPasswordAuthentication.checked = true + } + if (radioPasswordAuthentication.checked) { + root.chkSetUser.checked = true + } + } + } + } + + ImRadioButton { + id: radioPasswordAuthentication + enabled: chkSSH.checked + text: qsTr("Use password authentication") + onCheckedChanged: { + if (checked) { + root.chkSetUser.checked = true + //root.fieldUserPassword.forceActiveFocus() + } + } + } + ImRadioButton { + id: radioPubKeyAuthentication + enabled: chkSSH.checked + text: qsTr("Allow public-key authentication only") + onCheckedChanged: { + if (checked) { + if (root.chkSetUser.checked && root.fieldUserName.text == "pi" && root.fieldUserPassword.text.length == 0) { + root.chkSetUser.checked = false + } + } + } + } + + Text { + text: qsTr("Set authorized_keys for '%1':").arg(root.fieldUserName.text) + color: radioPubKeyAuthentication.checked ? "black" : "grey" + // textFormat: Text.PlainText + Layout.leftMargin: 40 + } + Item { + id: publicKeyListViewContainer + Layout.leftMargin: 40 + Layout.rightMargin: 5 + Layout.minimumHeight: 50 + Layout.fillHeight: true + Layout.preferredWidth: popup.width - (20 + Layout.leftMargin + Layout.rightMargin) + + ListView { + id: publicKeyList + model: ListModel { + id: publicKeyModel + + } + boundsBehavior: Flickable.StopAtBounds + anchors.fill: parent + spacing: 12 + clip: true + + delegate: RowLayout { + id: publicKeyItem + property int publicKeyModelIndex: index + height: 50 + + TextField { + id: contentField + enabled: radioPubKeyAuthentication.checked + validator: RegularExpressionValidator { regularExpression: /^ssh-(ed25519|rsa|dss|ecdsa) AAAA(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})( [A-Za-z0-9-\\\/]+@[A-Za-z0-9-\\\/]+)?/ } + text: model.publicKeyField + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + implicitWidth: publicKeyList.width - (removePublicKeyItem.width + 20) + + onEditingFinished: { + publicKeyModel.set(publicKeyModelIndex, {publicKeyField: contentField.text}) + } + } + ImButton { + id: removePublicKeyItem + text: qsTr("Delete Key") + enabled: radioPubKeyAuthentication.checked + Layout.minimumWidth: 100 + Layout.preferredWidth: 100 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + + onClicked: { + if (publicKeyModelIndex != -1) { + publicKeyModel.remove(publicKeyModelIndex) + publicKeyListViewContainer.implicitHeight -= 50 + publicKeyList.spacing + } + } + } + } + } + } + + RowLayout { + ImButton { + text: qsTr("RUN SSH-KEYGEN") + Layout.leftMargin: 40 + enabled: imageWriter.hasSshKeyGen() && !imageWriter.hasPubKey() + onClicked: { + enabled = false + imageWriter.generatePubKey() + publicKeyModel.append({publicKeyField: imageWriter.getDefaultPubKey()}) + } + } + ImButton { + text: qsTr("Add SSH Key") + Layout.leftMargin: 40 + enabled: radioPubKeyAuthentication.checked + onClicked: { + publicKeyModel.append({publicKeyField: ""}) + if (publicKeyListViewContainer.implicitHeight) { + publicKeyListViewContainer.implicitHeight += 50 + publicKeyList.spacing + } else { + publicKeyListViewContainer.implicitHeight += 100 + (publicKeyList.spacing) + } + } + } + } + } +} diff --git a/src/qml.qrc b/src/qml.qrc index 472ba551..70dae58b 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -33,6 +33,9 @@ icons/cat_3d_printing.png icons/logo_stacked_imager.png icons/logo_sxs_imager.png + OptionsGeneralTab.qml + OptionsServicesTab.qml + OptionsMiscTab.qml qmlcomponents/ImButton.qml qmlcomponents/ImButtonRed.qml qmlcomponents/ImCheckBox.qml From 0216d5d1c287e8b4ec8e054f4bcab8aae7c0435b Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Tue, 4 Feb 2025 15:41:27 +0000 Subject: [PATCH 05/47] ci: Add a .qmllint.ini file Warnings are fatal, any warning will make a CI run fail. We have 3 categories which are set to "Info" level. These will gradually be bumped to "Warning" level once we fix the warnings. qmllint will be called by CI --- .qmllint.ini | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .qmllint.ini diff --git a/.qmllint.ini b/.qmllint.ini new file mode 100644 index 00000000..066d519a --- /dev/null +++ b/.qmllint.ini @@ -0,0 +1,48 @@ +[General] +AdditionalQmlImportPaths= +DisableDefaultImports=false +MaxWarnings=0 +OverwriteImportTypes= +ResourcePath= + +[Warnings] +AccessSingletonViaObject=warning +Anchors=warning +AttachedPropertyReuse=warning +AttachedPropertyType=warning +BadSignalHandler=warning +BadSignalHandlerParameters=warning +CompilerWarnings=disable +ControlsAttachedPropertyReuse=warning +ControlsNativeCustomize=warning +Deprecated=warning +DuplicatePropertyBinding=warning +DuplicatedName=warning +ImportFailure=warning +IncompatibleType=warning +InheritanceCycle=warning +InvalidLintDirective=warning +LayoutsPositioning=warning +LintPluginWarnings=warning +MissingProperty=info +MissingType=warning +MultilineStrings=warning +NonListProperty=warning +PrefixedImportType=warning +PropertyAlias=warning +PropertyAliasCycles=warning +PropertyChangesParsed=warning +ReadOnlyProperty=warning +RequiredProperty=warning +RestrictedType=warning +TopLevelComponent=warning +TypeError=warning +UncreatableType=warning +UnexpectedVarType=warning +UnknownProperty=warning +UnqualifiedAccess=info +UnresolvedType=warning +UnusedImports=info +UseProperFunction=warning +VarUsedBeforeDeclaration=warning +WithStatement=warning From b48531b5a1f01f3b7c5d53d9469ff78bd18bfd1f Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Tue, 4 Feb 2025 18:35:35 +0000 Subject: [PATCH 06/47] Make each option tab inherit from OptionsTabBase.qml So they can share a few properties --- src/OptionsGeneralTab.qml | 13 +------------ src/OptionsMiscTab.qml | 14 +------------- src/OptionsServicesTab.qml | 14 +------------- src/OptionsTabBase.qml | 23 +++++++++++++++++++++++ src/qml.qrc | 1 + 5 files changed, 27 insertions(+), 38 deletions(-) create mode 100644 src/OptionsTabBase.qml diff --git a/src/OptionsGeneralTab.qml b/src/OptionsGeneralTab.qml index d4fd3221..d5d3227b 100644 --- a/src/OptionsGeneralTab.qml +++ b/src/OptionsGeneralTab.qml @@ -10,7 +10,7 @@ import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 import "qmlcomponents" -ScrollView { +OptionsTabBase { id: root property alias fieldHostname: fieldHostname @@ -31,17 +31,6 @@ ScrollView { required property bool sshEnabled required property bool passwordAuthenticationEnabled - font.family: roboto.name - - Layout.fillWidth: true - Layout.fillHeight: true - - property double scrollPosition - - // clip: true - ScrollBar.vertical.policy: ScrollBar.AsNeeded - ScrollBar.vertical.position: scrollPosition - scrollPosition: scrollPosition ColumnLayout { RowLayout { ImCheckBox { diff --git a/src/OptionsMiscTab.qml b/src/OptionsMiscTab.qml index 445f092a..cd6a7628 100644 --- a/src/OptionsMiscTab.qml +++ b/src/OptionsMiscTab.qml @@ -8,25 +8,13 @@ import QtQuick.Layouts 1.15 import QtQuick.Window 2.15 import "qmlcomponents" -ScrollView { +OptionsTabBase { id: root property alias beepEnabled: chkBeep.checked property alias telemetryEnabled: chkTelemtry.checked property alias ejectEnabled: chkEject.checked - font.family: roboto.name - - Layout.fillWidth: true - Layout.fillHeight: true - - property double scrollPosition - - // clip: true - ScrollBar.vertical.policy: ScrollBar.AsNeeded - ScrollBar.vertical.position: scrollPosition - scrollPosition: scrollPosition - ColumnLayout { ImCheckBox { id: chkBeep diff --git a/src/OptionsServicesTab.qml b/src/OptionsServicesTab.qml index 322b3075..7457b258 100644 --- a/src/OptionsServicesTab.qml +++ b/src/OptionsServicesTab.qml @@ -10,7 +10,7 @@ import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 import "qmlcomponents" -ScrollView { +OptionsTabBase { id: root property alias publicKeyModel: publicKeyModel @@ -19,18 +19,6 @@ ScrollView { property alias radioPasswordAuthentication: radioPasswordAuthentication property alias radioPubKeyAuthentication: radioPubKeyAuthentication - property double scrollPosition - - Layout.fillWidth: true - Layout.fillHeight: true - font.family: roboto.name - - - - // clip: true - ScrollBar.vertical.policy: ScrollBar.AsNeeded - ScrollBar.vertical.position: scrollPosition - scrollPosition: scrollPosition required property ImCheckBox chkSetUser required property TextField fieldUserName required property TextField fieldUserPassword diff --git a/src/OptionsTabBase.qml b/src/OptionsTabBase.qml new file mode 100644 index 00000000..67d36f4a --- /dev/null +++ b/src/OptionsTabBase.qml @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.15 + +ScrollView { + id: root + + property double scrollPosition + + font.family: roboto.name + Layout.fillWidth: true + Layout.fillHeight: true + + // clip: true + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ScrollBar.vertical.position: scrollPosition + scrollPosition: scrollPosition +} diff --git a/src/qml.qrc b/src/qml.qrc index 70dae58b..ff30ade0 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -36,6 +36,7 @@ OptionsGeneralTab.qml OptionsServicesTab.qml OptionsMiscTab.qml + OptionsTabBase.qml qmlcomponents/ImButton.qml qmlcomponents/ImButtonRed.qml qmlcomponents/ImCheckBox.qml From 449a232d09766f8063d329befc611a65c1a12c15 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Tue, 4 Feb 2025 16:28:30 +0000 Subject: [PATCH 07/47] Fix undefined behaviour regarding layouting Items inside a layout should not set size. The Image size was even misleading, since it's actually rendered as 40x40 (aka its implicit size). This was caught by qmllint: "Detected height on an item that is managed by a layout. This is undefined behavior; use implictHeight or Layout.preferredHeight instead. [Quick.layout-positioning]" --- src/main.qml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main.qml b/src/main.qml index 63dd21a9..c5dbdf6f 100644 --- a/src/main.qml +++ b/src/main.qml @@ -1076,7 +1076,7 @@ ApplicationWindow { anchors.fill: parent Item { - width: 25 + Layout.preferredWidth: 25 } Image { @@ -1084,12 +1084,10 @@ ApplicationWindow { source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg" verticalAlignment: Image.AlignVCenter fillMode: Image.Pad - width: 64 - height: 60 } Item { - width: 25 + Layout.preferredWidth: 25 } ColumnLayout { @@ -1108,7 +1106,6 @@ ApplicationWindow { } Text { textFormat: Text.StyledText - height: parent.height verticalAlignment: Text.AlignVCenter Layout.fillWidth: true font.family: roboto.name From fe93fda3b8f7b2d7f87021215f4912b38cee8128 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 5 Feb 2025 10:51:45 +0000 Subject: [PATCH 08/47] Make options popup modal So we don't get into weird situations where it disappears into the background --- src/OptionsPopup.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index 0f06fe6a..0b3fc302 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -25,6 +25,8 @@ Window { title: qsTr("OS Customization") + modality: Qt.WindowModal + property bool initialized: false property bool hasSavedSettings: false property string config From 263540c2153c916b12310a89520d6295256a8137 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Tue, 4 Feb 2025 17:25:14 +0000 Subject: [PATCH 09/47] Remove unused imports and make the warning fatal CI will catch any future unused imports --- .qmllint.ini | 2 +- src/qmlcomponents/ImButton.qml | 1 - src/qmlcomponents/ImButtonRed.qml | 3 --- src/qmlcomponents/ImCheckBox.qml | 1 - src/qmlcomponents/ImRadioButton.qml | 1 - 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.qmllint.ini b/.qmllint.ini index 066d519a..84821442 100644 --- a/.qmllint.ini +++ b/.qmllint.ini @@ -42,7 +42,7 @@ UnexpectedVarType=warning UnknownProperty=warning UnqualifiedAccess=info UnresolvedType=warning -UnusedImports=info +UnusedImports=warning UseProperFunction=warning VarUsedBeforeDeclaration=warning WithStatement=warning diff --git a/src/qmlcomponents/ImButton.qml b/src/qmlcomponents/ImButton.qml index 431c0a17..51ce29bb 100644 --- a/src/qmlcomponents/ImButton.qml +++ b/src/qmlcomponents/ImButton.qml @@ -5,7 +5,6 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 Button { diff --git a/src/qmlcomponents/ImButtonRed.qml b/src/qmlcomponents/ImButtonRed.qml index 3f47014c..35d376f0 100644 --- a/src/qmlcomponents/ImButtonRed.qml +++ b/src/qmlcomponents/ImButtonRed.qml @@ -3,9 +3,6 @@ * Copyright (C) 2022 Raspberry Pi Ltd */ -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 ImButton { diff --git a/src/qmlcomponents/ImCheckBox.qml b/src/qmlcomponents/ImCheckBox.qml index 03863610..5bb7a89b 100644 --- a/src/qmlcomponents/ImCheckBox.qml +++ b/src/qmlcomponents/ImCheckBox.qml @@ -5,7 +5,6 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 CheckBox { diff --git a/src/qmlcomponents/ImRadioButton.qml b/src/qmlcomponents/ImRadioButton.qml index 717da987..20c6dfc9 100644 --- a/src/qmlcomponents/ImRadioButton.qml +++ b/src/qmlcomponents/ImRadioButton.qml @@ -5,7 +5,6 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 RadioButton { From 5d1e494064cdbb99ddaf9cd5999136262a5f13f3 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 5 Feb 2025 11:01:46 +0000 Subject: [PATCH 10/47] Fix 2 unqualified access in OptionsPopup less qmllint noise --- src/OptionsPopup.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index 0b3fc302..4f112c56 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -155,8 +155,8 @@ Window { } } - applySettings() - saveSettings() + popup.applySettings() + popup.saveSettings() popup.close() } } From fb10bcab9d6a7b1ac9adbdfa2813853a1fcd7363 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 5 Feb 2025 15:18:06 +0000 Subject: [PATCH 11/47] Use qt_add_qml_module This is the modern way to use QML. - Adds cmake target to run qmlint during build - C++ type registration is now automatic - No more manually writing qmldir files and type files - Allows to use QML_ELEMENT and friends, which allows tooling to warn us when calling unknown methods (from QML to C++), ie: proper code-model in QML when using C++ types. - No need to manually add .qml files to qrc - Can enable qmlcachegen, disabled for now. - All kind of options you can toggle, just check docs for qt_add_qml_module Tidied the qml.qrc, now separates C++ related assets from QML ones. Also since the QML assets need to live in the same prefix as the module. --- src/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- src/qml.qrc | 29 ++++++++++++----------------- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc2f1c8d..c81ac678 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -307,6 +307,40 @@ else() add_definitions(-DCHECK_VERSION_DEFAULT=false) endif() +qt_policy(SET QTP0001 NEW) + +if (QT_KNOWN_POLICY_QTP0004) + qt_policy(SET QTP0004 NEW) +endif() + +set(IMAGER_QML_FILES + main.qml + MsgPopup.qml + OptionsGeneralTab.qml + OptionsMiscTab.qml + OptionsPopup.qml + OptionsServicesTab.qml + OptionsTabBase.qml + Style.qml + UseSavedSettingsPopup.qml + qmlcomponents/ImButton.qml + qmlcomponents/ImButtonRed.qml + qmlcomponents/ImCheckBox.qml + qmlcomponents/ImPopup.qml + qmlcomponents/ImRadioButton.qml +) + +qt_add_qml_module(${PROJECT_NAME} + URI RpiImager + VERSION 1.0 + QML_FILES ${IMAGER_QML_FILES} + NO_CACHEGEN + OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/RpiImager + DEPENDENCIES QtQuick + NO_PLUGIN + RESOURCE_PREFIX "/" # Puts the stuff into qrc:/RpiImager/ +) + # Because dependencies are typically not available by default on Windows, build bundled code if (WIN32) # Target Windows 10, in line with Qt requirements diff --git a/src/main.cpp b/src/main.cpp index 25250e3d..d5a0ecf1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -343,7 +343,7 @@ int main(int argc, char *argv[]) engine.setNetworkAccessManagerFactory(&namf); engine.rootContext()->setContextProperty("imageWriter", &imageWriter); engine.rootContext()->setContextProperty("driveListModel", imageWriter.getDriveList()); - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + engine.load(QUrl(QStringLiteral("qrc:/RpiImager/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; diff --git a/src/qml.qrc b/src/qml.qrc index ff30ade0..22017ae8 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -1,17 +1,26 @@ + + - main.qml + countries.txt + timezones.txt + keymap-layouts.txt + + + + qtquickcontrols2.conf - icons/rpi-imager.ico + fonts/Roboto-Bold.ttf fonts/Roboto-Light.ttf fonts/Roboto-Regular.ttf + + icons/rpi-imager.ico icons/ic_chevron_left_40px.svg icons/ic_chevron_right_40px.svg icons/ic_sd_storage_40px.svg icons/ic_storage_40px.svg icons/ic_usb_40px.svg - MsgPopup.qml icons/use_custom.png icons/erase.png icons/cat_raspberry_pi_os.png @@ -20,28 +29,14 @@ icons/cat_misc_utility_images.png icons/cat_media_players.png icons/cat_emulation_and_games.png - OptionsPopup.qml - countries.txt - timezones.txt - UseSavedSettingsPopup.qml icons/ic_info_16px.png icons/ic_info_12px.png - keymap-layouts.txt icons/ic_cog_red.svg icons/cat_home_automation.png icons/cat_language_specific_operating_systems.png icons/cat_3d_printing.png icons/logo_stacked_imager.png icons/logo_sxs_imager.png - OptionsGeneralTab.qml - OptionsServicesTab.qml - OptionsMiscTab.qml - OptionsTabBase.qml - qmlcomponents/ImButton.qml - qmlcomponents/ImButtonRed.qml - qmlcomponents/ImCheckBox.qml - qmlcomponents/ImPopup.qml - qmlcomponents/ImRadioButton.qml icons/cat_digital_signage.png From 67e5337caf6557e11efa1bf5734d23167f6cda9d Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 5 Feb 2025 16:55:07 +0000 Subject: [PATCH 12/47] Register our C++ types ImageWriter and DriveListModel were exposed to QML but they were not using QML_ELEMENT annotation. This unleashes the full power of the QML codemodel which now knows about the C++ methods. qmllint can warn about missing methods or typos. In a way it makes our QML usage more "statically typed". Port away from context properties, otherwise none of the above works. And also since context properties are discouraged since Qt 6. --- src/CMakeLists.txt | 8 +++++- src/drivelistmodel.h | 3 +++ src/imagewriter.h | 4 +++ src/main.cpp | 7 ++--- src/main.qml | 63 ++++++++++++++++++++++++-------------------- src/qml.qrc | 3 +-- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c81ac678..d0757531 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -243,7 +243,7 @@ if( IS_BIG_ENDIAN ) message( FATAL_ERROR "We currently only support 'little endian' CPU architectures" ) endif( IS_BIG_ENDIAN ) -set(SOURCES ${PLATFORM_SOURCES} "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.cpp" +set(SOURCES ${PLATFORM_SOURCES} "main.cpp" "networkaccessmanagerfactory.cpp" "drivelistitem.cpp" "drivelistmodel.cpp" "drivelistmodelpollthread.cpp" "downloadthread.cpp" "downloadextractthread.cpp" "devicewrapper.cpp" "devicewrapperblockcacheentry.cpp" "devicewrapperpartition.cpp" "devicewrapperfatpartition.cpp" "driveformatthread.cpp" "localfileextractthread.cpp" "powersaveblocker.cpp" "downloadstatstelemetry.cpp" "qml.qrc" "dependencies/sha256crypt/sha256crypt.c" "cli.cpp") @@ -330,10 +330,16 @@ set(IMAGER_QML_FILES qmlcomponents/ImRadioButton.qml ) +# C++ types exposed to QML +set(IMAGER_QML_CPP_TYPES + imagewriter.cpp +) + qt_add_qml_module(${PROJECT_NAME} URI RpiImager VERSION 1.0 QML_FILES ${IMAGER_QML_FILES} + SOURCES ${IMAGER_QML_CPP_TYPES} NO_CACHEGEN OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/RpiImager DEPENDENCIES QtQuick diff --git a/src/drivelistmodel.h b/src/drivelistmodel.h index 3cef3e06..2816f2e6 100644 --- a/src/drivelistmodel.h +++ b/src/drivelistmodel.h @@ -9,12 +9,15 @@ #include #include #include +#include #include "drivelistitem.h" #include "drivelistmodelpollthread.h" class DriveListModel : public QAbstractListModel { Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("Created by C++") public: DriveListModel(QObject *parent = nullptr); virtual int rowCount(const QModelIndex &) const; diff --git a/src/imagewriter.h b/src/imagewriter.h index 843ed539..3036556b 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include "config.h" #include "powersaveblocker.h" #include "drivelistmodel.h" @@ -28,6 +30,8 @@ class QTranslator; class ImageWriter : public QObject { Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("Created by C++") public: explicit ImageWriter(QObject *parent = nullptr); virtual ~ImageWriter(); diff --git a/src/main.cpp b/src/main.cpp index d5a0ecf1..a7df72c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -145,7 +145,7 @@ int main(int argc, char *argv[]) /** QtQuick on QT5 exhibits spurious disk cache failures that cannot be * resolved by a user in a trivial manner (they have to delete the cache manually). - * + * * This flag can potentially noticeably increase the start time of the application, however * between this and a hard-to-detect spurious failure affecting Linux, macOS and Windows, * this trade is the one most likely to result in a good experience for the widest group @@ -341,8 +341,9 @@ int main(int argc, char *argv[]) imageWriter.setSrc(url); imageWriter.setEngine(&engine); engine.setNetworkAccessManagerFactory(&namf); - engine.rootContext()->setContextProperty("imageWriter", &imageWriter); - engine.rootContext()->setContextProperty("driveListModel", imageWriter.getDriveList()); + + engine.setInitialProperties(QVariantMap{{"imageWriter", QVariant::fromValue(&imageWriter)}, + {"driveListModel", QVariant::fromValue(imageWriter.getDriveList())}}); engine.load(QUrl(QStringLiteral("qrc:/RpiImager/main.qml"))); if (engine.rootObjects().isEmpty()) diff --git a/src/main.qml b/src/main.qml index c5dbdf6f..83d5ee54 100644 --- a/src/main.qml +++ b/src/main.qml @@ -10,10 +10,15 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 import "qmlcomponents" +import RpiImager + ApplicationWindow { id: window visible: true + required property ImageWriter imageWriter + required property DriveListModel driveListModel + width: imageWriter.isEmbeddedMode() ? -1 : 680 height: imageWriter.isEmbeddedMode() ? -1 : 450 minimumWidth: imageWriter.isEmbeddedMode() ? -1 : 680 @@ -165,7 +170,7 @@ ApplicationWindow { ImButton { id: osbutton - text: imageWriter.srcFileName() === "" ? qsTr("CHOOSE OS") : imageWriter.srcFileName() + text: window.imageWriter.srcFileName() === "" ? qsTr("CHOOSE OS") : window.imageWriter.srcFileName() spacing: 0 padding: 0 bottomPadding: 0 @@ -211,7 +216,7 @@ ApplicationWindow { Layout.preferredWidth: 200 Layout.fillWidth: true onClicked: { - imageWriter.startDriveListPolling() + window.imageWriter.startDriveListPolling() dstpopup.open() dstlist.forceActiveFocus() } @@ -265,7 +270,7 @@ ApplicationWindow { onClicked: { enabled = false progressText.text = qsTr("Cancelling...") - imageWriter.cancelWrite() + window.imageWriter.cancelWrite() } Layout.alignment: Qt.AlignRight visible: false @@ -280,7 +285,7 @@ ApplicationWindow { onClicked: { enabled = false progressText.text = qsTr("Finalizing...") - imageWriter.setVerifyEnabled(false) + window.imageWriter.setVerifyEnabled(false) } Layout.alignment: Qt.AlignRight visible: false @@ -297,11 +302,11 @@ ApplicationWindow { Accessible.description: qsTr("Select this button to start writing the image") enabled: false onClicked: { - if (!imageWriter.readyToWrite()) { + if (!window.imageWriter.readyToWrite()) { return } - if (!optionspopup.visible && imageWriter.imageSupportsCustomization()) { + if (!optionspopup.visible && window.imageWriter.imageSupportsCustomization()) { usesavedsettingspopup.openPopup() } else { confirmwritepopup.askForConfirmation() @@ -315,8 +320,8 @@ ApplicationWindow { color: "#ffffff" font.pixelSize: 18 font.family: roboto.name - visible: imageWriter.isEmbeddedMode() && imageWriter.customRepo() - text: qsTr("Using custom repository: %1").arg(imageWriter.constantOsListUrl()) + visible: window.imageWriter.isEmbeddedMode() && window.imageWriter.customRepo() + text: qsTr("Using custom repository: %1").arg(window.imageWriter.constantOsListUrl()) } Text { @@ -325,7 +330,7 @@ ApplicationWindow { color: "#ffffff" font.pixelSize: 18 font.family: roboto.name - visible: imageWriter.isEmbeddedMode() + visible: window.imageWriter.isEmbeddedMode() text: qsTr("Network not ready yet") } @@ -334,7 +339,7 @@ ApplicationWindow { color: "#ffffff" font.pixelSize: 18 font.family: roboto.name - visible: !imageWriter.hasMouse() + visible: !window.imageWriter.hasMouse() text: qsTr("Keyboard navigation: navigate to next button press button/select item go up/down in lists") } @@ -343,7 +348,7 @@ ApplicationWindow { Layout.columnSpan: 3 Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom Layout.bottomMargin: 5 - visible: imageWriter.isEmbeddedMode() + visible: window.imageWriter.isEmbeddedMode() implicitWidth: langbar.width implicitHeight: langbar.height color: "#ffffe3" @@ -364,14 +369,14 @@ ApplicationWindow { ComboBox { font.pixelSize: 12 font.family: roboto.name - model: imageWriter.getTranslations() + model: window.imageWriter.getTranslations() Layout.preferredWidth: 200 currentIndex: -1 Component.onCompleted: { - currentIndex = find(imageWriter.getCurrentLanguage()) + currentIndex = find(window.imageWriter.getCurrentLanguage()) } onActivated: { - imageWriter.changeLanguage(editText) + window.imageWriter.changeLanguage(editText) } Layout.topMargin: 10 Layout.bottomMargin: 10 @@ -384,16 +389,16 @@ ApplicationWindow { Layout.bottomMargin: 10 } ComboBox { - enabled: imageWriter.isEmbeddedMode() + enabled: window.imageWriter.isEmbeddedMode() font.pixelSize: 12 font.family: roboto.name - model: imageWriter.getKeymapLayoutList() + model: window.imageWriter.getKeymapLayoutList() currentIndex: -1 Component.onCompleted: { - currentIndex = find(imageWriter.getCurrentKeyboard()) + currentIndex = find(window.imageWriter.getCurrentKeyboard()) } onActivated: { - imageWriter.changeKeyboard(editText) + window.imageWriter.changeKeyboard(editText) } Layout.topMargin: 10 Layout.bottomMargin: 10 @@ -934,7 +939,7 @@ ApplicationWindow { height: parent.height-50 padding: 0 closePolicy: Popup.CloseOnEscape - onClosed: imageWriter.stopDriveListPolling() + onClosed: window.imageWriter.stopDriveListPolling() // background of title Rectangle { @@ -985,7 +990,7 @@ ApplicationWindow { } ListView { id: dstlist - model: driveListModel + model: window.driveListModel delegate: dstdelegate anchors.top: dstpopup_title_separator.bottom @@ -1196,8 +1201,8 @@ ApplicationWindow { osbutton.enabled = false dstbutton.enabled = false hwbutton.enabled = false - imageWriter.setVerifyEnabled(true) - imageWriter.startWrite() + window.imageWriter.setVerifyEnabled(true) + window.imageWriter.startWrite() } function askForConfirmation() @@ -1229,7 +1234,7 @@ ApplicationWindow { minimumHeight: 400 id: optionspopup onSaveSettingsSignal: { - imageWriter.setSavedCustomizationSettings(settings) + window.imageWriter.setSavedCustomizationSettings(settings) usesavedsettingspopup.hasSavedSettings = true } } @@ -1242,13 +1247,13 @@ ApplicationWindow { confirmwritepopup.askForConfirmation() } onNo: { - imageWriter.setImageCustomization("", "", "", "", "") + window.imageWriter.setImageCustomization("", "", "", "", "") confirmwritepopup.askForConfirmation() } onNoClearSettings: { hasSavedSettings = false optionspopup.clearCustomizationFields() - imageWriter.clearSavedCustomizationSettings() + window.imageWriter.clearSavedCustomizationSettings() confirmwritepopup.askForConfirmation() } onEditSettings: { @@ -1584,15 +1589,15 @@ ApplicationWindow { property string drive : "" interval: 100 onTriggered: { - for (var i = 0; i < driveListModel.rowCount(); i++) + for (var i = 0; i < window.driveListModel.rowCount(); i++) { /* FIXME: there should be a better way to iterate drivelist than fetch data by numeric role number */ - if (driveListModel.data(driveListModel.index(i,0), 0x101) === drive) { + if (window.driveListModel.data(window.driveListModel.index(i,0), 0x101) === drive) { selectDstItem({ device: drive, - description: driveListModel.data(driveListModel.index(i,0), 0x102), - size: driveListModel.data(driveListModel.index(i,0), 0x103), + description: window.driveListModel.data(window.driveListModel.index(i,0), 0x102), + size: window.driveListModel.data(window.driveListModel.index(i,0), 0x103), readonly: false }) break diff --git a/src/qml.qrc b/src/qml.qrc index 22017ae8..c55c541f 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -2,6 +2,7 @@ + qtquickcontrols2.conf countries.txt timezones.txt keymap-layouts.txt @@ -9,8 +10,6 @@ - qtquickcontrols2.conf - fonts/Roboto-Bold.ttf fonts/Roboto-Light.ttf fonts/Roboto-Regular.ttf From 1a655364f723e47f2dfa1a6b77d9dda57d583ff9 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 5 Feb 2025 18:11:58 +0000 Subject: [PATCH 13/47] Add a Style singleton For now just stores the FontLoader. Already helps silencing loads of qmllint warnings regarding unqualified access to the font loader. --- src/CMakeLists.txt | 2 ++ src/MsgPopup.qml | 4 +-- src/OptionsTabBase.qml | 2 +- src/Style.qml | 21 ++++++++++++++ src/UseSavedSettingsPopup.qml | 2 +- src/main.qml | 50 ++++++++++++++++------------------ src/qmlcomponents/ImButton.qml | 2 +- src/qmlcomponents/ImPopup.qml | 4 +-- 8 files changed, 53 insertions(+), 34 deletions(-) create mode 100644 src/Style.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d0757531..b85674c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -330,6 +330,8 @@ set(IMAGER_QML_FILES qmlcomponents/ImRadioButton.qml ) +set_source_files_properties(Style.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) + # C++ types exposed to QML set(IMAGER_QML_CPP_TYPES imagewriter.cpp diff --git a/src/MsgPopup.qml b/src/MsgPopup.qml index 0d7e2939..c468cb66 100644 --- a/src/MsgPopup.qml +++ b/src/MsgPopup.qml @@ -28,7 +28,7 @@ ImPopup { font.pointSize: 12 wrapMode: Text.Wrap textFormat: Text.StyledText - font.family: roboto.name + font.family: Style.fontFamily Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: 10 @@ -77,7 +77,7 @@ ImPopup { onClicked: { Qt.quit() } - font.family: roboto.name + font.family: Style.fontFamily visible: root.quitButton } } diff --git a/src/OptionsTabBase.qml b/src/OptionsTabBase.qml index 67d36f4a..5f959cb7 100644 --- a/src/OptionsTabBase.qml +++ b/src/OptionsTabBase.qml @@ -12,7 +12,7 @@ ScrollView { property double scrollPosition - font.family: roboto.name + font.family: Style.fontFamily Layout.fillWidth: true Layout.fillHeight: true diff --git a/src/Style.qml b/src/Style.qml new file mode 100644 index 00000000..b4d2d2e0 --- /dev/null +++ b/src/Style.qml @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2025 Raspberry Pi Ltd + */ + +pragma Singleton + +import QtQuick 2.15 + +Item { + id: root + + readonly property alias fontFamily: roboto.name + readonly property alias fontFamilyLight: robotoLight.name + readonly property alias fontFamilyBold: robotoBold.name + + FontLoader { id: roboto; source: "fonts/Roboto-Regular.ttf" } + FontLoader { id: robotoLight; source: "fonts/Roboto-Light.ttf" } + FontLoader { id: robotoBold; source: "fonts/Roboto-Bold.ttf" } + +} diff --git a/src/UseSavedSettingsPopup.qml b/src/UseSavedSettingsPopup.qml index 55944fe9..404eafec 100644 --- a/src/UseSavedSettingsPopup.qml +++ b/src/UseSavedSettingsPopup.qml @@ -28,7 +28,7 @@ ImPopup { font.pointSize: 12 wrapMode: Text.Wrap textFormat: Text.StyledText - font.family: roboto.name + font.family: Style.fontFamily Layout.fillHeight: true Layout.leftMargin: 25 Layout.rightMargin: 25 diff --git a/src/main.qml b/src/main.qml index 83d5ee54..402eac7d 100644 --- a/src/main.qml +++ b/src/main.qml @@ -26,10 +26,6 @@ ApplicationWindow { title: qsTr("Raspberry Pi Imager v%1").arg(imageWriter.constantVersion()) - FontLoader {id: roboto; source: "fonts/Roboto-Regular.ttf"} - FontLoader {id: robotoLight; source: "fonts/Roboto-Light.ttf"} - FontLoader {id: robotoBold; source: "fonts/Roboto-Bold.ttf"} - onClosing: { if (progressBar.visible) { close.accepted = false @@ -126,7 +122,7 @@ ApplicationWindow { Layout.preferredHeight: 17 Layout.preferredWidth: 100 font.pixelSize: 12 - font.family: robotoBold.name + font.family: Style.fontFamilyBold font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -163,7 +159,7 @@ ApplicationWindow { Layout.fillWidth: true Layout.preferredHeight: 17 font.pixelSize: 12 - font.family: robotoBold.name + font.family: Style.fontFamilyBold font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -200,7 +196,7 @@ ApplicationWindow { Layout.fillWidth: true Layout.preferredHeight: 17 font.pixelSize: 12 - font.family: robotoBold.name + font.family: Style.fontFamilyBold font.bold: true horizontalAlignment: Text.AlignHCenter } @@ -236,7 +232,7 @@ ApplicationWindow { id: progressText font.pointSize: 10 color: "white" - font.family: robotoBold.name + font.family: Style.fontFamilyBold font.bold: true visible: false horizontalAlignment: Text.AlignHCenter @@ -319,7 +315,7 @@ ApplicationWindow { Layout.columnSpan: 3 color: "#ffffff" font.pixelSize: 18 - font.family: roboto.name + font.family: Style.fontFamily visible: window.imageWriter.isEmbeddedMode() && window.imageWriter.customRepo() text: qsTr("Using custom repository: %1").arg(window.imageWriter.constantOsListUrl()) } @@ -329,7 +325,7 @@ ApplicationWindow { Layout.columnSpan: 3 color: "#ffffff" font.pixelSize: 18 - font.family: roboto.name + font.family: Style.fontFamily visible: window.imageWriter.isEmbeddedMode() text: qsTr("Network not ready yet") } @@ -338,7 +334,7 @@ ApplicationWindow { Layout.columnSpan: 3 color: "#ffffff" font.pixelSize: 18 - font.family: roboto.name + font.family: Style.fontFamily visible: !window.imageWriter.hasMouse() text: qsTr("Keyboard navigation: navigate to next button press button/select item go up/down in lists") } @@ -360,7 +356,7 @@ ApplicationWindow { Text { font.pixelSize: 12 - font.family: roboto.name + font.family: Style.fontFamily text: qsTr("Language: ") Layout.leftMargin: 30 Layout.topMargin: 10 @@ -368,7 +364,7 @@ ApplicationWindow { } ComboBox { font.pixelSize: 12 - font.family: roboto.name + font.family: Style.fontFamily model: window.imageWriter.getTranslations() Layout.preferredWidth: 200 currentIndex: -1 @@ -383,7 +379,7 @@ ApplicationWindow { } Text { font.pixelSize: 12 - font.family: roboto.name + font.family: Style.fontFamily text: qsTr("Keyboard: ") Layout.topMargin: 10 Layout.bottomMargin: 10 @@ -391,7 +387,7 @@ ApplicationWindow { ComboBox { enabled: window.imageWriter.isEmbeddedMode() font.pixelSize: 12 - font.family: roboto.name + font.family: Style.fontFamily model: window.imageWriter.getKeymapLayoutList() currentIndex: -1 Component.onCompleted: { @@ -458,7 +454,7 @@ ApplicationWindow { horizontalAlignment: Text.AlignHCenter anchors.fill: parent anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true } @@ -471,7 +467,7 @@ ApplicationWindow { anchors.top: parent.top anchors.rightMargin: 25 anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true MouseArea { @@ -558,7 +554,7 @@ ApplicationWindow { horizontalAlignment: Text.AlignHCenter anchors.fill: parent anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true } @@ -571,7 +567,7 @@ ApplicationWindow { anchors.top: parent.top anchors.rightMargin: 25 anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true MouseArea { @@ -769,13 +765,13 @@ ApplicationWindow { Text { text: name elide: Text.ElideRight - font.family: roboto.name + font.family: Style.fontFamily font.bold: true } Text { Layout.fillWidth: true - font.family: roboto.name + font.family: Style.fontFamily text: description wrapMode: Text.WordWrap color: "#1a1a1a" @@ -862,7 +858,7 @@ ApplicationWindow { Text { text: name elide: Text.ElideRight - font.family: roboto.name + font.family: Style.fontFamily font.bold: true } Image { @@ -882,7 +878,7 @@ ApplicationWindow { Text { Layout.fillWidth: true - font.family: roboto.name + font.family: Style.fontFamily text: description wrapMode: Text.WordWrap color: "#1a1a1a" @@ -955,7 +951,7 @@ ApplicationWindow { horizontalAlignment: Text.AlignHCenter anchors.fill: parent anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true } @@ -968,7 +964,7 @@ ApplicationWindow { anchors.top: parent.top anchors.rightMargin: 25 anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true MouseArea { @@ -1100,7 +1096,7 @@ ApplicationWindow { textFormat: Text.StyledText verticalAlignment: Text.AlignVCenter Layout.fillWidth: true - font.family: roboto.name + font.family: Style.fontFamily font.pointSize: 16 color: !dstitem.unselectable ? "" : "grey"; text: { @@ -1113,7 +1109,7 @@ ApplicationWindow { textFormat: Text.StyledText verticalAlignment: Text.AlignVCenter Layout.fillWidth: true - font.family: roboto.name + font.family: Style.fontFamily font.pointSize: 12 color: !dstitem.unselectable ? "" : "grey"; text: { diff --git a/src/qmlcomponents/ImButton.qml b/src/qmlcomponents/ImButton.qml index 51ce29bb..2b36b110 100644 --- a/src/qmlcomponents/ImButton.qml +++ b/src/qmlcomponents/ImButton.qml @@ -8,7 +8,7 @@ import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.2 Button { - font.family: roboto.name + font.family: Style.fontFamily font.capitalization: Font.AllUppercase Material.background: activeFocus ? "#d1dcfb" : "#ffffff" Material.foreground: "#cd2355" diff --git a/src/qmlcomponents/ImPopup.qml b/src/qmlcomponents/ImPopup.qml index fd3e75f3..7e7e4531 100644 --- a/src/qmlcomponents/ImPopup.qml +++ b/src/qmlcomponents/ImPopup.qml @@ -38,7 +38,7 @@ Popup { horizontalAlignment: Text.AlignHCenter anchors.fill: parent anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true } @@ -51,7 +51,7 @@ Popup { anchors.top: parent.top anchors.rightMargin: 25 anchors.topMargin: 10 - font.family: roboto.name + font.family: Style.fontFamily font.bold: true MouseArea { From c845ffe3fe6eae20d1b53ac1587b7af1682a473b Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 11:40:33 +0000 Subject: [PATCH 14/47] Split main.qml into multiple files Each popup is now self-contained in its own file, bundling the delegate the model and the view and only exposing what main.qml really needs to access. --- src/CMakeLists.txt | 3 + src/DstPopup.qml | 255 +++++++++++++ src/HwPopup.qml | 261 +++++++++++++ src/OSPopup.qml | 418 +++++++++++++++++++++ src/main.qml | 914 +-------------------------------------------- 5 files changed, 950 insertions(+), 901 deletions(-) create mode 100644 src/DstPopup.qml create mode 100644 src/HwPopup.qml create mode 100644 src/OSPopup.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b85674c5..b328cf00 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -328,6 +328,9 @@ set(IMAGER_QML_FILES qmlcomponents/ImCheckBox.qml qmlcomponents/ImPopup.qml qmlcomponents/ImRadioButton.qml + HwPopup.qml + OSPopup.qml + DstPopup.qml ) set_source_files_properties(Style.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) diff --git a/src/DstPopup.qml b/src/DstPopup.qml new file mode 100644 index 00000000..aaca74a4 --- /dev/null +++ b/src/DstPopup.qml @@ -0,0 +1,255 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.15 +import QtQuick.Controls.Material 2.2 +import QtQuick.Window 2.15 + +Popup { + id: root + x: 50 + y: 25 + width: parent.width-100 + height: parent.height-50 + padding: 0 + closePolicy: Popup.CloseOnEscape + onClosed: window.imageWriter.stopDriveListPolling() + + property alias dstlist: dstlist + + // background of title + Rectangle { + id: dstpopup_title_background + color: "#f5f5f5" + anchors.left: parent.left + anchors.top: parent.top + height: 35 + width: parent.width + + Text { + text: qsTr("Storage") + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + } + + Text { + text: "X" + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + root.close() + } + } + } + } + // line under title + Rectangle { + id: dstpopup_title_separator + color: "#afafaf" + width: parent.width + anchors.top: dstpopup_title_background.bottom + height: 1 + } + ListView { + id: dstlist + model: window.driveListModel + delegate: dstdelegate + + anchors.top: dstpopup_title_separator.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: filterRow.top + boundsBehavior: Flickable.StopAtBounds + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + clip: true + + Label { + anchors.fill: parent + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + visible: parent.count == 0 + text: qsTr("No storage devices found") + font.bold: true + } + + ScrollBar.vertical: ScrollBar { + width: 10 + policy: dstlist.contentHeight > dstlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + } + + Keys.onSpacePressed: { + if (currentIndex == -1) + return + selectDstItem(currentItem) + } + Accessible.onPressAction: { + if (currentIndex == -1) + return + selectDstItem(currentItem) + } + Keys.onEnterPressed: Keys.onSpacePressed(event) + Keys.onReturnPressed: Keys.onSpacePressed(event) + } + RowLayout { + id: filterRow + anchors { + bottom: parent.bottom + right: parent.right + left: parent.left + } + Item { + Layout.fillWidth: true + } + ImCheckBox { + id: filterSystemDrives + checked: true + text: qsTr("Exclude System Drives") + } + } + + Component { + id: dstdelegate + + Item { + id: dstitem + anchors.left: parent.left + anchors.right: parent.right + Layout.topMargin: 1 + height: 61 + Accessible.name: { + var txt = description+" - "+(size/1000000000).toFixed(1)+ " " + qsTr("gigabytes") + if (mountpoints.length > 0) { + txt += qsTr("Mounted as %1").arg(mountpoints.join(", ")) + } + return txt; + } + property string description: model.description + property string device: model.device + property string size: model.size + property bool unselectable: (isSystem && filterSystemDrives.checked) || isReadOnly + + Rectangle { + id: dstbgrect + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: 60 + + color: mouseOver ? "#f5f5f5" : "#ffffff" + property bool mouseOver: false + + RowLayout { + anchors.fill: parent + + Item { + Layout.preferredWidth: 25 + } + + Image { + id: dstitem_image + source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg" + verticalAlignment: Image.AlignVCenter + fillMode: Image.Pad + } + + Item { + Layout.preferredWidth: 25 + } + + ColumnLayout { + Text { + textFormat: Text.StyledText + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + font.family: Style.fontFamily + font.pointSize: 16 + color: !dstitem.unselectable ? "" : "grey"; + text: { + var sizeStr = (size/1000000000).toFixed(1)+ " " + qsTr("GB"); + return description + " - " + sizeStr; + } + + } + Text { + textFormat: Text.StyledText + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + font.family: Style.fontFamily + font.pointSize: 12 + color: !dstitem.unselectable ? "" : "grey"; + text: { + var txt= qsTr("Mounted as %1").arg(mountpoints.join(", ")); + if (isReadOnly) { + txt += " " + qsTr("[WRITE PROTECTED]"); + } else if (isSystem) { + text += " [" + qsTr("SYSTEM") + "]"; + } + return txt; + } + } + } + } + + } + Rectangle { + id: dstborderrect + anchors.top: dstbgrect.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#dcdcdc" + } + + MouseArea { + anchors.fill: parent + cursorShape: !dstitem.unselectable ? Qt.PointingHandCursor : Qt.ForbiddenCursor + hoverEnabled: true + enabled: !dstitem.unselectable + + onEntered: { + dstbgrect.mouseOver = true + } + + onExited: { + dstbgrect.mouseOver = false + } + + onClicked: { + selectDstItem(model) + } + } + } + } + + function selectDstItem(d) { + if (d.isReadOnly) { + onError(qsTr("SD card is write protected.
Push the lock switch on the left side of the card upwards, and try again.")) + return + } + + dstpopup.close() + imageWriter.setDst(d.device, d.size) + dstbutton.text = d.description + if (imageWriter.readyToWrite()) { + writebutton.enabled = true + } + } +} diff --git a/src/HwPopup.qml b/src/HwPopup.qml new file mode 100644 index 00000000..3e4fbdfc --- /dev/null +++ b/src/HwPopup.qml @@ -0,0 +1,261 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.15 +import QtQuick.Controls.Material 2.2 +import QtQuick.Window 2.15 + +Popup { + id: root + x: 50 + y: 25 + width: parent.width-100 + height: parent.height-50 + padding: 0 + closePolicy: Popup.CloseOnEscape + property string hwselected: "" + property alias deviceModel: deviceModel + property alias hwlist: hwlist + required property ListView oslist + required property SwipeView osswipeview + + // background of title + Rectangle { + id: hwpopup_title_background + color: "#f5f5f5" + anchors.left: parent.left + anchors.top: parent.top + height: 35 + width: parent.width + + Text { + text: qsTr("Raspberry Pi Device") + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + } + + Text { + text: "X" + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + root.close() + } + } + } + } + // line under title + Rectangle { + id: hwpopup_title_separator + color: "#afafaf" + width: parent.width + anchors.top: hwpopup_title_background.bottom + height: 1 + } + + ListView { + id: hwlist + clip: true + model: ListModel { + id: deviceModel + ListElement { + name: qsTr("[ All ]") + tags: "[]" + icon: "" + description: "" + matching_type: "exclusive" + } + } + currentIndex: -1 + delegate: hwdelegate + anchors.top: hwpopup_title_separator.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + boundsBehavior: Flickable.StopAtBounds + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + ScrollBar.vertical: ScrollBar { + anchors.right: parent.right + width: 10 + policy: hwlist.contentHeight > hwlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + } + Keys.onSpacePressed: { + if (currentIndex != -1) + root.selectHWitem(model.get(currentIndex)) + } + Accessible.onPressAction: { + if (currentIndex != -1) + root.selectHWitem(model.get(currentIndex)) + } + Keys.onEnterPressed: Keys.onSpacePressed(event) + Keys.onReturnPressed: Keys.onSpacePressed(event) + } + + Component { + id: hwdelegate + + Item { + width: window.width-100 + height: contentLayout.implicitHeight + 24 + Accessible.name: name+".\n"+description + + MouseArea { + id: hwMouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + bgrect.mouseOver = true + } + + onExited: { + bgrect.mouseOver = false + } + + onClicked: { + selectHWitem(model) + } + } + + Rectangle { + id: bgrect + anchors.fill: parent + color: "#f5f5f5" + visible: mouseOver && parent.ListView.view.currentIndex !== index + property bool mouseOver: false + } + Rectangle { + id: borderrect + implicitHeight: 1 + implicitWidth: parent.width + color: "#dcdcdc" + y: parent.height + } + + RowLayout { + id: contentLayout + anchors { + left: parent.left + top: parent.top + right: parent.right + margins: 12 + } + spacing: 12 + + Image { + source: typeof icon === "undefined" ? "" : icon + Layout.preferredHeight: 64 + Layout.preferredWidth: 64 + sourceSize.width: 64 + sourceSize.height: 64 + fillMode: Image.PreserveAspectFit + verticalAlignment: Image.AlignVCenter + Layout.alignment: Qt.AlignVCenter + } + ColumnLayout { + Layout.fillWidth: true + + Text { + text: name + elide: Text.ElideRight + font.family: Style.fontFamily + font.bold: true + } + + Text { + Layout.fillWidth: true + font.family: Style.fontFamily + text: description + wrapMode: Text.WordWrap + color: "#1a1a1a" + } + + ToolTip { + visible: hwMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" + delay: 1000 + text: typeof(tooltip) == "string" ? tooltip : "" + clip: false + } + } + } + } + } + + function selectHWitem(hwmodel) { + /* Default is exclusive matching */ + var inclusive = false + + if (hwmodel.matching_type) { + switch (hwmodel.matching_type) { + case "exclusive": + break; + case "inclusive": + inclusive = true + break; + } + } + + imageWriter.setHWFilterList(hwmodel.tags, inclusive) + + /* Reload list */ + var oslist_json = imageWriter.getFilteredOSlist(); + var o = JSON.parse(oslist_json) + var oslist_parsed = oslistFromJson(o) + if (oslist_parsed === false) + return + + /* As we're filtering the OS list, we need to ensure we present a 'Recommended' OS. + * To do this, we exploit a convention of how we build the OS list. By convention, + * the preferred OS for a device is listed at the top level of the list, and is at the + * lowest index. So.. + */ + if (oslist_parsed.length != 0) { + var candidate = oslist_parsed[0] + + if ("description" in candidate && + !("subitems" in candidate) && + !candidate["description"].includes("(Recommended)") + ) + { + candidate["description"] += " (Recommended)" + } + } + + osmodel.clear() + for (var i in oslist_parsed) { + osmodel.append(oslist_parsed[i]) + } + + // When the HW device is changed, reset the OS selection otherwise + // you get a weird effect with the selection moving around in the list + // when the user next opens the OS list, and the user could still have + // an OS selected which isn't compatible with this HW device + oslist.currentIndex = -1 + osswipeview.currentIndex = 0 + imageWriter.setSrc("") + osbutton.text = qsTr("CHOOSE OS") + writebutton.enabled = false + + hwbutton.text = hwmodel.name + root.close() + } +} diff --git a/src/OSPopup.qml b/src/OSPopup.qml new file mode 100644 index 00000000..d5ab383e --- /dev/null +++ b/src/OSPopup.qml @@ -0,0 +1,418 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.15 +import QtQuick.Controls.Material 2.2 +import QtQuick.Window 2.15 + +Popup { + id: root + x: 50 + y: 25 + width: parent.width-100 + height: parent.height-50 + padding: 0 + closePolicy: Popup.CloseOnEscape + property string categorySelected : "" + property alias oslist: oslist + property alias osswipeview: osswipeview + + // background of title + Rectangle { + id: ospopup_title_background + color: "#f5f5f5" + anchors.left: parent.left + anchors.top: parent.top + height: 35 + width: parent.width + + Text { + text: qsTr("Operating System") + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + } + + Text { + text: "X" + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + root.close() + osswipeview.decrementCurrentIndex() + } + } + } + } + // line under title + Rectangle { + id: ospopup_title_separator + color: "#afafaf" + width: parent.width + anchors.top: ospopup_title_background.bottom + height: 1 + } + + SwipeView { + anchors.top: ospopup_title_separator.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + id: osswipeview + interactive: false + clip: true + + ListView { + id: oslist + model: osmodel + currentIndex: -1 + delegate: osdelegate + anchors.top: parent.top + anchors.left: parent.left + anchors.bottom: parent.bottom + width: root.width + boundsBehavior: Flickable.StopAtBounds + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + ScrollBar.vertical: ScrollBar { + anchors.right: parent.right + width: 10 + policy: oslist.contentHeight > oslist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + } + Keys.onSpacePressed: { + if (currentIndex != -1) + root.selectOSitem(model.get(currentIndex), true) + } + Accessible.onPressAction: { + if (currentIndex != -1) + root.selectOSitem(model.get(currentIndex), true) + } + Keys.onEnterPressed: (event) => { Keys.spacePressed(event) } + Keys.onReturnPressed: (event) => { Keys.spacePressed(event) } + Keys.onRightPressed: { + // Navigate into sublists but don't select an OS entry + if (currentIndex != -1 && root.isOSsublist(model.get(currentIndex))) + root.selectOSitem(model.get(currentIndex), true) + } + } + } + + Component { + id: suboslist + + ListView { + model: ListModel { + ListElement { + url: "" + icon: "icons/ic_chevron_left_40px.svg" + extract_size: 0 + image_download_size: 0 + extract_sha256: "" + contains_multiple_files: false + release_date: "" + subitems_url: "internal://back" + subitems_json: "" + name: qsTr("Back") + description: qsTr("Go back to main menu") + tooltip: "" + website: "" + init_format: "" + } + } + + currentIndex: -1 + delegate: osdelegate + + boundsBehavior: Flickable.StopAtBounds + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + ScrollBar.vertical: ScrollBar { + width: 10 + policy: parent.contentHeight > parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + } + Keys.onSpacePressed: { + if (currentIndex != -1) + selectOSitem(model.get(currentIndex)) + } + Accessible.onPressAction: { + if (currentIndex != -1) + selectOSitem(model.get(currentIndex)) + } + Keys.onEnterPressed: Keys.onSpacePressed(event) + Keys.onReturnPressed: Keys.onSpacePressed(event) + Keys.onRightPressed: { + // Navigate into sublists but don't select an OS entry + if (currentIndex != -1 && isOSsublist(model.get(currentIndex))) + selectOSitem(model.get(currentIndex), true) + } + Keys.onLeftPressed: { + osswipeview.decrementCurrentIndex() + ospopup.categorySelected = "" + } + } + } + + Component { + id: osdelegate + + Item { + width: window.width-100 + height: contentLayout.implicitHeight + 24 + Accessible.name: name+".\n"+description + + MouseArea { + id: osMouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + + onEntered: { + bgrect.mouseOver = true + } + + onExited: { + bgrect.mouseOver = false + } + + onClicked: { + selectOSitem(model) + } + } + + Rectangle { + id: bgrect + anchors.fill: parent + color: "#f5f5f5" + visible: mouseOver && parent.ListView.view.currentIndex !== index + property bool mouseOver: false + } + Rectangle { + id: borderrect + implicitHeight: 1 + implicitWidth: parent.width + color: "#dcdcdc" + y: parent.height + } + + RowLayout { + id: contentLayout + anchors { + left: parent.left + top: parent.top + right: parent.right + margins: 12 + } + spacing: 12 + + Image { + source: icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png": icon + Layout.preferredHeight: 40 + Layout.preferredWidth: 40 + sourceSize.width: 40 + sourceSize.height: 40 + fillMode: Image.PreserveAspectFit + verticalAlignment: Image.AlignVCenter + Layout.alignment: Qt.AlignVCenter + } + ColumnLayout { + Layout.fillWidth: true + + RowLayout { + spacing: 12 + Text { + text: name + elide: Text.ElideRight + font.family: Style.fontFamily + font.bold: true + } + Image { + source: "icons/ic_info_16px.png" + Layout.preferredHeight: 16 + Layout.preferredWidth: 16 + visible: typeof(website) == "string" && website + MouseArea { + anchors.fill: parent + onClicked: Qt.openUrlExternally(website) + } + } + Item { + Layout.fillWidth: true + } + } + + Text { + Layout.fillWidth: true + font.family: Style.fontFamily + text: description + wrapMode: Text.WordWrap + color: "#1a1a1a" + } + + Text { + Layout.fillWidth: true + elide: Text.ElideRight + color: "#646464" + font.weight: Font.Light + visible: typeof(release_date) == "string" && release_date + text: qsTr("Released: %1").arg(release_date) + } + Text { + Layout.fillWidth: true + elide: Text.ElideRight + color: "#646464" + font.weight: Font.Light + visible: typeof(url) == "string" && url != "" && url != "internal://format" + text: !url ? "" : + typeof(extract_sha256) != "undefined" && imageWriter.isCached(url,extract_sha256) + ? qsTr("Cached on your computer") + : url.startsWith("file://") + ? qsTr("Local file") + : qsTr("Online - %1 GB download").arg((image_download_size/1073741824).toFixed(1)) + } + + ToolTip { + visible: osMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" + delay: 1000 + text: typeof(tooltip) == "string" ? tooltip : "" + clip: false + } + } + Image { + source: "icons/ic_chevron_right_40px.svg" + visible: (typeof(subitems_json) == "string" && subitems_json != "") || (typeof(subitems_url) == "string" && subitems_url != "" && subitems_url != "internal://back") + Layout.preferredHeight: 40 + Layout.preferredWidth: 40 + fillMode: Image.PreserveAspectFit + } + } + } + } + + function selectNamedOS(name, collection) + { + for (var i = 0; i < collection.count; i++) { + var os = collection.get(i) + + if (typeof(os.subitems_json) == "string" && os.subitems_json != "") { + selectNamedOS(name, os.subitems_json) + } + else if (typeof(os.url) !== "undefined" && name === os.name) { + selectOSitem(os, false) + break + } + } + } + + function selectOSitem(d, selectFirstSubitem) + { + if (typeof(d.subitems_json) == "string" && d.subitems_json !== "") { + var m = newSublist() + var subitems = JSON.parse(d.subitems_json) + + for (var i in subitems) + { + var entry = subitems[i]; + if ("subitems" in entry) { + /* Flatten sub-subitems entry */ + entry["subitems_json"] = JSON.stringify(entry["subitems"]) + delete entry["subitems"] + } + m.append(entry) + } + + osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 + osswipeview.incrementCurrentIndex() + ospopup.categorySelected = d.name + } else if (typeof(d.subitems_url) == "string" && d.subitems_url !== "") { + if (d.subitems_url === "internal://back") + { + osswipeview.decrementCurrentIndex() + ospopup.categorySelected = "" + } + else + { + console.log("Failure: Backend should have pre-flattened the JSON!"); + + osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 + osswipeview.incrementCurrentIndex() + } + } else if (d.url === "") { + if (!imageWriter.isEmbeddedMode()) { + imageWriter.openFileDialog() + } + else { + if (imageWriter.mountUsbSourceMedia()) { + var m = newSublist() + + var usboslist = JSON.parse(imageWriter.getUsbSourceOSlist()) + for (var i in usboslist) { + m.append(usboslist[i]) + } + osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 + osswipeview.incrementCurrentIndex() + } + else + { + onError(qsTr("Connect an USB stick containing images first.
The images must be located in the root folder of the USB stick.")) + } + } + } else { + imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, ospopup.categorySelected, d.name, typeof(d.init_format) != "undefined" ? d.init_format : "") + osbutton.text = d.name + ospopup.close() + osswipeview.decrementCurrentIndex() + if (imageWriter.readyToWrite()) { + writebutton.enabled = true + } + } + } + + function newSublist() { + if (osswipeview.currentIndex == (osswipeview.count-1)) + { + var newlist = suboslist.createObject(osswipeview) + osswipeview.addItem(newlist) + } + + var m = osswipeview.itemAt(osswipeview.currentIndex+1).model + + if (m.count>1) + { + m.remove(1, m.count-1) + } + + return m + } + + /// Is the item a sub-list or sub-sub-list in the OS selection model? + function isOSsublist(d) { + // Top level category + if (typeof(d.subitems_json) == "string" && d.subitems_json !== "") { + return true + } + + // Sub-category + if (typeof(d.subitems_url) == "string" && d.subitems_url !== "" + && d.subitems_url !== "internal://back") + { + return true + } + + return false + } +} diff --git a/src/main.qml b/src/main.qml index 402eac7d..86ed5dac 100644 --- a/src/main.qml +++ b/src/main.qml @@ -138,7 +138,7 @@ ApplicationWindow { Layout.fillWidth: true onClicked: { hwpopup.open() - hwlist.forceActiveFocus() + hwpopup.hwlist.forceActiveFocus() } Accessible.ignored: ospopup.visible || dstpopup.visible || hwpopup.visible Accessible.description: qsTr("Select this button to choose your target Raspberry Pi") @@ -175,7 +175,7 @@ ApplicationWindow { Layout.fillWidth: true onClicked: { ospopup.open() - osswipeview.currentItem.forceActiveFocus() + ospopup.osswipeview.currentItem.forceActiveFocus() } Accessible.ignored: ospopup.visible || dstpopup.visible || hwpopup.visible Accessible.description: qsTr("Select this button to change the operating system") @@ -214,7 +214,7 @@ ApplicationWindow { onClicked: { window.imageWriter.startDriveListPolling() dstpopup.open() - dstlist.forceActiveFocus() + dstpopup.dstlist.forceActiveFocus() } Accessible.ignored: ospopup.visible || dstpopup.visible || hwpopup.visible Accessible.description: qsTr("Select this button to change the destination storage device") @@ -430,261 +430,14 @@ ApplicationWindow { } } - Popup { + HwPopup { id: hwpopup - x: 50 - y: 25 - width: parent.width-100 - height: parent.height-50 - padding: 0 - closePolicy: Popup.CloseOnEscape - property string hwselected: "" - - // background of title - Rectangle { - id: hwpopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - text: qsTr("Raspberry Pi Device") - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - } - - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - hwpopup.close() - } - } - } - } - // line under title - Rectangle { - id: hwpopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: hwpopup_title_background.bottom - height: 1 - } - - ListView { - id: hwlist - clip: true - model: ListModel { - id: deviceModel - ListElement { - name: qsTr("[ All ]") - tags: "[]" - icon: "" - description: "" - matching_type: "exclusive" - } - } - currentIndex: -1 - delegate: hwdelegate - anchors.top: hwpopup_title_separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - ScrollBar.vertical: ScrollBar { - anchors.right: parent.right - width: 10 - policy: hwlist.contentHeight > hwlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } - Keys.onSpacePressed: { - if (currentIndex != -1) - selectHWitem(model.get(currentIndex)) - } - Accessible.onPressAction: { - if (currentIndex != -1) - selectHWitem(model.get(currentIndex)) - } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) - } + oslist: ospopup.oslist + osswipeview: ospopup.osswipeview } - /* - Popup for OS selection - */ - Popup { + OSPopup { id: ospopup - x: 50 - y: 25 - width: parent.width-100 - height: parent.height-50 - padding: 0 - closePolicy: Popup.CloseOnEscape - property string categorySelected : "" - - // background of title - Rectangle { - id: ospopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - text: qsTr("Operating System") - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - } - - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - ospopup.close() - osswipeview.decrementCurrentIndex() - } - } - } - } - // line under title - Rectangle { - id: ospopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: ospopup_title_background.bottom - height: 1 - } - - SwipeView { - anchors.top: ospopup_title_separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - id: osswipeview - interactive: false - clip: true - - ListView { - id: oslist - model: osmodel - currentIndex: -1 - delegate: osdelegate - anchors.top: parent.top - anchors.left: parent.left - anchors.bottom: parent.bottom - width: ospopup.width - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - ScrollBar.vertical: ScrollBar { - anchors.right: parent.right - width: 10 - policy: oslist.contentHeight > oslist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } - Keys.onSpacePressed: { - if (currentIndex != -1) - selectOSitem(model.get(currentIndex), true) - } - Accessible.onPressAction: { - if (currentIndex != -1) - selectOSitem(model.get(currentIndex), true) - } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) - Keys.onRightPressed: { - // Navigate into sublists but don't select an OS entry - if (currentIndex != -1 && isOSsublist(model.get(currentIndex))) - selectOSitem(model.get(currentIndex), true) - } - } - } - } - - Component { - id: suboslist - - ListView { - model: ListModel { - ListElement { - url: "" - icon: "icons/ic_chevron_left_40px.svg" - extract_size: 0 - image_download_size: 0 - extract_sha256: "" - contains_multiple_files: false - release_date: "" - subitems_url: "internal://back" - subitems_json: "" - name: qsTr("Back") - description: qsTr("Go back to main menu") - tooltip: "" - website: "" - init_format: "" - } - } - - currentIndex: -1 - delegate: osdelegate - - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - ScrollBar.vertical: ScrollBar { - width: 10 - policy: parent.contentHeight > parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } - Keys.onSpacePressed: { - if (currentIndex != -1) - selectOSitem(model.get(currentIndex)) - } - Accessible.onPressAction: { - if (currentIndex != -1) - selectOSitem(model.get(currentIndex)) - } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) - Keys.onRightPressed: { - // Navigate into sublists but don't select an OS entry - if (currentIndex != -1 && isOSsublist(model.get(currentIndex))) - selectOSitem(model.get(currentIndex), true) - } - Keys.onLeftPressed: { - osswipeview.decrementCurrentIndex() - ospopup.categorySelected = "" - } - } } ListModel { @@ -697,463 +450,8 @@ ApplicationWindow { } } - Component { - id: hwdelegate - - Item { - width: window.width-100 - height: contentLayout.implicitHeight + 24 - Accessible.name: name+".\n"+description - - MouseArea { - id: hwMouseArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - - onEntered: { - bgrect.mouseOver = true - } - - onExited: { - bgrect.mouseOver = false - } - - onClicked: { - selectHWitem(model) - } - } - - Rectangle { - id: bgrect - anchors.fill: parent - color: "#f5f5f5" - visible: mouseOver && parent.ListView.view.currentIndex !== index - property bool mouseOver: false - } - Rectangle { - id: borderrect - implicitHeight: 1 - implicitWidth: parent.width - color: "#dcdcdc" - y: parent.height - } - - RowLayout { - id: contentLayout - anchors { - left: parent.left - top: parent.top - right: parent.right - margins: 12 - } - spacing: 12 - - Image { - source: typeof icon === "undefined" ? "" : icon - Layout.preferredHeight: 64 - Layout.preferredWidth: 64 - sourceSize.width: 64 - sourceSize.height: 64 - fillMode: Image.PreserveAspectFit - verticalAlignment: Image.AlignVCenter - Layout.alignment: Qt.AlignVCenter - } - ColumnLayout { - Layout.fillWidth: true - - Text { - text: name - elide: Text.ElideRight - font.family: Style.fontFamily - font.bold: true - } - - Text { - Layout.fillWidth: true - font.family: Style.fontFamily - text: description - wrapMode: Text.WordWrap - color: "#1a1a1a" - } - - ToolTip { - visible: hwMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" - delay: 1000 - text: typeof(tooltip) == "string" ? tooltip : "" - clip: false - } - } - } - } - } - - Component { - id: osdelegate - - Item { - width: window.width-100 - height: contentLayout.implicitHeight + 24 - Accessible.name: name+".\n"+description - - MouseArea { - id: osMouseArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - - onEntered: { - bgrect.mouseOver = true - } - - onExited: { - bgrect.mouseOver = false - } - - onClicked: { - selectOSitem(model) - } - } - - Rectangle { - id: bgrect - anchors.fill: parent - color: "#f5f5f5" - visible: mouseOver && parent.ListView.view.currentIndex !== index - property bool mouseOver: false - } - Rectangle { - id: borderrect - implicitHeight: 1 - implicitWidth: parent.width - color: "#dcdcdc" - y: parent.height - } - - RowLayout { - id: contentLayout - anchors { - left: parent.left - top: parent.top - right: parent.right - margins: 12 - } - spacing: 12 - - Image { - source: icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png": icon - Layout.preferredHeight: 40 - Layout.preferredWidth: 40 - sourceSize.width: 40 - sourceSize.height: 40 - fillMode: Image.PreserveAspectFit - verticalAlignment: Image.AlignVCenter - Layout.alignment: Qt.AlignVCenter - } - ColumnLayout { - Layout.fillWidth: true - - RowLayout { - spacing: 12 - Text { - text: name - elide: Text.ElideRight - font.family: Style.fontFamily - font.bold: true - } - Image { - source: "icons/ic_info_16px.png" - Layout.preferredHeight: 16 - Layout.preferredWidth: 16 - visible: typeof(website) == "string" && website - MouseArea { - anchors.fill: parent - onClicked: Qt.openUrlExternally(website) - } - } - Item { - Layout.fillWidth: true - } - } - - Text { - Layout.fillWidth: true - font.family: Style.fontFamily - text: description - wrapMode: Text.WordWrap - color: "#1a1a1a" - } - - Text { - Layout.fillWidth: true - elide: Text.ElideRight - color: "#646464" - font.weight: Font.Light - visible: typeof(release_date) == "string" && release_date - text: qsTr("Released: %1").arg(release_date) - } - Text { - Layout.fillWidth: true - elide: Text.ElideRight - color: "#646464" - font.weight: Font.Light - visible: typeof(url) == "string" && url != "" && url != "internal://format" - text: !url ? "" : - typeof(extract_sha256) != "undefined" && imageWriter.isCached(url,extract_sha256) - ? qsTr("Cached on your computer") - : url.startsWith("file://") - ? qsTr("Local file") - : qsTr("Online - %1 GB download").arg((image_download_size/1073741824).toFixed(1)) - } - - ToolTip { - visible: osMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" - delay: 1000 - text: typeof(tooltip) == "string" ? tooltip : "" - clip: false - } - } - Image { - source: "icons/ic_chevron_right_40px.svg" - visible: (typeof(subitems_json) == "string" && subitems_json != "") || (typeof(subitems_url) == "string" && subitems_url != "" && subitems_url != "internal://back") - Layout.preferredHeight: 40 - Layout.preferredWidth: 40 - fillMode: Image.PreserveAspectFit - } - } - } - } - - /* - Popup for storage device selection - */ - Popup { + DstPopup { id: dstpopup - x: 50 - y: 25 - width: parent.width-100 - height: parent.height-50 - padding: 0 - closePolicy: Popup.CloseOnEscape - onClosed: window.imageWriter.stopDriveListPolling() - - // background of title - Rectangle { - id: dstpopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - text: qsTr("Storage") - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - } - - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - dstpopup.close() - } - } - } - } - // line under title - Rectangle { - id: dstpopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: dstpopup_title_background.bottom - height: 1 - } - ListView { - id: dstlist - model: window.driveListModel - delegate: dstdelegate - - anchors.top: dstpopup_title_separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: filterRow.top - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - clip: true - - Label { - anchors.fill: parent - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - visible: parent.count == 0 - text: qsTr("No storage devices found") - font.bold: true - } - - ScrollBar.vertical: ScrollBar { - width: 10 - policy: dstlist.contentHeight > dstlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } - - Keys.onSpacePressed: { - if (currentIndex == -1) - return - selectDstItem(currentItem) - } - Accessible.onPressAction: { - if (currentIndex == -1) - return - selectDstItem(currentItem) - } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) - } - RowLayout { - id: filterRow - anchors { - bottom: parent.bottom - right: parent.right - left: parent.left - } - Item { - Layout.fillWidth: true - } - ImCheckBox { - id: filterSystemDrives - checked: true - text: qsTr("Exclude System Drives") - } - } - } - - Component { - id: dstdelegate - - Item { - id: dstitem - anchors.left: parent.left - anchors.right: parent.right - Layout.topMargin: 1 - height: 61 - Accessible.name: { - var txt = description+" - "+(size/1000000000).toFixed(1)+ " " + qsTr("gigabytes") - if (mountpoints.length > 0) { - txt += qsTr("Mounted as %1").arg(mountpoints.join(", ")) - } - return txt; - } - property string description: model.description - property string device: model.device - property string size: model.size - property bool unselectable: (isSystem && filterSystemDrives.checked) || isReadOnly - - Rectangle { - id: dstbgrect - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: 60 - - color: mouseOver ? "#f5f5f5" : "#ffffff" - property bool mouseOver: false - - RowLayout { - anchors.fill: parent - - Item { - Layout.preferredWidth: 25 - } - - Image { - id: dstitem_image - source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg" - verticalAlignment: Image.AlignVCenter - fillMode: Image.Pad - } - - Item { - Layout.preferredWidth: 25 - } - - ColumnLayout { - Text { - textFormat: Text.StyledText - verticalAlignment: Text.AlignVCenter - Layout.fillWidth: true - font.family: Style.fontFamily - font.pointSize: 16 - color: !dstitem.unselectable ? "" : "grey"; - text: { - var sizeStr = (size/1000000000).toFixed(1)+ " " + qsTr("GB"); - return description + " - " + sizeStr; - } - - } - Text { - textFormat: Text.StyledText - verticalAlignment: Text.AlignVCenter - Layout.fillWidth: true - font.family: Style.fontFamily - font.pointSize: 12 - color: !dstitem.unselectable ? "" : "grey"; - text: { - var txt= qsTr("Mounted as %1").arg(mountpoints.join(", ")); - if (isReadOnly) { - txt += " " + qsTr("[WRITE PROTECTED]"); - } else if (isSystem) { - text += " [" + qsTr("SYSTEM") + "]"; - } - return txt; - } - } - } - } - - } - Rectangle { - id: dstborderrect - anchors.top: dstbgrect.bottom - anchors.left: parent.left - anchors.right: parent.right - height: 1 - color: "#dcdcdc" - } - - MouseArea { - anchors.fill: parent - cursorShape: !dstitem.unselectable ? Qt.PointingHandCursor : Qt.ForbiddenCursor - hoverEnabled: true - enabled: !dstitem.unselectable - - onEntered: { - dstbgrect.mouseOver = true - } - - onExited: { - dstbgrect.mouseOver = false - } - - onClicked: { - selectDstItem(model) - } - } - } } MsgPopup { @@ -1354,7 +652,7 @@ ApplicationWindow { imageWriter.setSrc(file) osbutton.text = imageWriter.srcFileName() ospopup.close() - osswipeview.decrementCurrentIndex() + ospopup.osswipeview.decrementCurrentIndex() if (imageWriter.readyToWrite()) { writebutton.enabled = true } @@ -1513,21 +811,6 @@ ApplicationWindow { return oslist_parsed } - function selectNamedOS(name, collection) - { - for (var i = 0; i < collection.count; i++) { - var os = collection.get(i) - - if (typeof(os.subitems_json) == "string" && os.subitems_json != "") { - selectNamedOS(name, os.subitems_json) - } - else if (typeof(os.url) !== "undefined" && name === os.name) { - selectOSitem(os, false) - break - } - } - } - function fetchOSlist() { var oslist_json = imageWriter.getFilteredOSlist(); var o = JSON.parse(oslist_json) @@ -1544,15 +827,15 @@ ApplicationWindow { if ("devices" in imager) { - deviceModel.clear() + hwpopup.deviceModel.clear() var devices = imager["devices"] for (var j in devices) { devices[j]["tags"] = JSON.stringify(devices[j]["tags"]) - deviceModel.append(devices[j]) + hwpopup.deviceModel.append(devices[j]) if ("default" in devices[j] && devices[j]["default"]) { - hwlist.currentIndex = deviceModel.count-1 + hwpopup.hwlist.currentIndex = hwpopup.deviceModel.count-1 } } } @@ -1590,7 +873,7 @@ ApplicationWindow { /* FIXME: there should be a better way to iterate drivelist than fetch data by numeric role number */ if (window.driveListModel.data(window.driveListModel.index(i,0), 0x101) === drive) { - selectDstItem({ + dstpopup.selectDstItem({ device: drive, description: window.driveListModel.data(window.driveListModel.index(i,0), 0x102), size: window.driveListModel.data(window.driveListModel.index(i,0), 0x103), @@ -1601,175 +884,4 @@ ApplicationWindow { } } } - - function newSublist() { - if (osswipeview.currentIndex == (osswipeview.count-1)) - { - var newlist = suboslist.createObject(osswipeview) - osswipeview.addItem(newlist) - } - - var m = osswipeview.itemAt(osswipeview.currentIndex+1).model - - if (m.count>1) - { - m.remove(1, m.count-1) - } - - return m - } - - function selectHWitem(hwmodel) { - /* Default is exclusive matching */ - var inclusive = false - - if (hwmodel.matching_type) { - switch (hwmodel.matching_type) { - case "exclusive": - break; - case "inclusive": - inclusive = true - break; - } - } - - imageWriter.setHWFilterList(hwmodel.tags, inclusive) - - /* Reload list */ - var oslist_json = imageWriter.getFilteredOSlist(); - var o = JSON.parse(oslist_json) - var oslist_parsed = oslistFromJson(o) - if (oslist_parsed === false) - return - - /* As we're filtering the OS list, we need to ensure we present a 'Recommended' OS. - * To do this, we exploit a convention of how we build the OS list. By convention, - * the preferred OS for a device is listed at the top level of the list, and is at the - * lowest index. So.. - */ - if (oslist_parsed.length != 0) { - var candidate = oslist_parsed[0] - - if ("description" in candidate && - !("subitems" in candidate) && - !candidate["description"].includes("(Recommended)") - ) - { - candidate["description"] += " (Recommended)" - } - } - - osmodel.clear() - for (var i in oslist_parsed) { - osmodel.append(oslist_parsed[i]) - } - - // When the HW device is changed, reset the OS selection otherwise - // you get a weird effect with the selection moving around in the list - // when the user next opens the OS list, and the user could still have - // an OS selected which isn't compatible with this HW device - oslist.currentIndex = -1 - osswipeview.currentIndex = 0 - imageWriter.setSrc("") - osbutton.text = qsTr("CHOOSE OS") - writebutton.enabled = false - - hwbutton.text = hwmodel.name - hwpopup.close() - } - - /// Is the item a sub-list or sub-sub-list in the OS selection model? - function isOSsublist(d) { - // Top level category - if (typeof(d.subitems_json) == "string" && d.subitems_json !== "") { - return true - } - - // Sub-category - if (typeof(d.subitems_url) == "string" && d.subitems_url !== "" - && d.subitems_url !== "internal://back") - { - return true - } - - return false - } - - function selectOSitem(d, selectFirstSubitem) - { - if (typeof(d.subitems_json) == "string" && d.subitems_json !== "") { - var m = newSublist() - var subitems = JSON.parse(d.subitems_json) - - for (var i in subitems) - { - var entry = subitems[i]; - if ("subitems" in entry) { - /* Flatten sub-subitems entry */ - entry["subitems_json"] = JSON.stringify(entry["subitems"]) - delete entry["subitems"] - } - m.append(entry) - } - - osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 - osswipeview.incrementCurrentIndex() - ospopup.categorySelected = d.name - } else if (typeof(d.subitems_url) == "string" && d.subitems_url !== "") { - if (d.subitems_url === "internal://back") - { - osswipeview.decrementCurrentIndex() - ospopup.categorySelected = "" - } - else - { - console.log("Failure: Backend should have pre-flattened the JSON!"); - - osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 - osswipeview.incrementCurrentIndex() - } - } else if (d.url === "") { - if (!imageWriter.isEmbeddedMode()) { - imageWriter.openFileDialog() - } - else { - if (imageWriter.mountUsbSourceMedia()) { - var m = newSublist() - - var usboslist = JSON.parse(imageWriter.getUsbSourceOSlist()) - for (var i in usboslist) { - m.append(usboslist[i]) - } - osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 - osswipeview.incrementCurrentIndex() - } - else - { - onError(qsTr("Connect an USB stick containing images first.
The images must be located in the root folder of the USB stick.")) - } - } - } else { - imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, ospopup.categorySelected, d.name, typeof(d.init_format) != "undefined" ? d.init_format : "") - osbutton.text = d.name - ospopup.close() - osswipeview.decrementCurrentIndex() - if (imageWriter.readyToWrite()) { - writebutton.enabled = true - } - } - } - - function selectDstItem(d) { - if (d.isReadOnly) { - onError(qsTr("SD card is write protected.
Push the lock switch on the left side of the card upwards, and try again.")) - return - } - - dstpopup.close() - imageWriter.setDst(d.device, d.size) - dstbutton.text = d.description - if (imageWriter.readyToWrite()) { - writebutton.enabled = true - } - } } From 77cf6427f16cc5028e182982c4030cbf884761f5 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 13:06:01 +0000 Subject: [PATCH 15/47] Make the 3 main popups inherit a common base Removes good chunk of code triplication --- src/CMakeLists.txt | 1 + src/DstPopup.qml | 58 +++--------------------------------- src/HwPopup.qml | 60 +++---------------------------------- src/MainPopupBase.qml | 69 +++++++++++++++++++++++++++++++++++++++++++ src/OSPopup.qml | 60 ++++--------------------------------- 5 files changed, 84 insertions(+), 164 deletions(-) create mode 100644 src/MainPopupBase.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b328cf00..1784cda3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -328,6 +328,7 @@ set(IMAGER_QML_FILES qmlcomponents/ImCheckBox.qml qmlcomponents/ImPopup.qml qmlcomponents/ImRadioButton.qml + MainPopupBase.qml HwPopup.qml OSPopup.qml DstPopup.qml diff --git a/src/DstPopup.qml b/src/DstPopup.qml index aaca74a4..e920dac2 100644 --- a/src/DstPopup.qml +++ b/src/DstPopup.qml @@ -9,71 +9,21 @@ import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 -Popup { +MainPopupBase { id: root - x: 50 - y: 25 - width: parent.width-100 - height: parent.height-50 - padding: 0 - closePolicy: Popup.CloseOnEscape + onClosed: window.imageWriter.stopDriveListPolling() property alias dstlist: dstlist - // background of title - Rectangle { - id: dstpopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - text: qsTr("Storage") - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - } - - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true + title: qsTr("Storage") - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - root.close() - } - } - } - } - // line under title - Rectangle { - id: dstpopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: dstpopup_title_background.bottom - height: 1 - } ListView { id: dstlist model: window.driveListModel delegate: dstdelegate - anchors.top: dstpopup_title_separator.bottom + anchors.top: root.title_separator.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: filterRow.top diff --git a/src/HwPopup.qml b/src/HwPopup.qml index 3e4fbdfc..26e8a316 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -9,67 +9,15 @@ import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 -Popup { +MainPopupBase { id: root - x: 50 - y: 25 - width: parent.width-100 - height: parent.height-50 - padding: 0 - closePolicy: Popup.CloseOnEscape + property string hwselected: "" property alias deviceModel: deviceModel property alias hwlist: hwlist required property ListView oslist required property SwipeView osswipeview - - // background of title - Rectangle { - id: hwpopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - text: qsTr("Raspberry Pi Device") - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - } - - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - root.close() - } - } - } - } - // line under title - Rectangle { - id: hwpopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: hwpopup_title_background.bottom - height: 1 - } + title: qsTr("Raspberry Pi Device") ListView { id: hwlist @@ -86,7 +34,7 @@ Popup { } currentIndex: -1 delegate: hwdelegate - anchors.top: hwpopup_title_separator.bottom + anchors.top: root.title_separator.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom diff --git a/src/MainPopupBase.qml b/src/MainPopupBase.qml new file mode 100644 index 00000000..dd29688c --- /dev/null +++ b/src/MainPopupBase.qml @@ -0,0 +1,69 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2025 Raspberry Pi Ltd + */ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick.Controls.Material + +Popup { + id: root + x: 50 + y: 25 + width: parent.width-100 + height: parent.height-50 + padding: 0 + closePolicy: Popup.CloseOnEscape + + required property string title + property alias title_separator: title_separator + + // background of title + Rectangle { + id: title_background + color: "#f5f5f5" + anchors.left: parent.left + anchors.top: parent.top + height: 35 + width: parent.width + + Text { + text: root.title + horizontalAlignment: Text.AlignHCenter + anchors.fill: parent + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + } + + Text { + text: "X" + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: Style.fontFamily + font.bold: true + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + root.close() + } + } + } + } + // line under title + Rectangle { + id: title_separator + color: "#afafaf" + width: parent.width + anchors.top: title_background.bottom + height: 1 + } +} diff --git a/src/OSPopup.qml b/src/OSPopup.qml index d5ab383e..c1aaf28f 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -9,69 +9,21 @@ import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 -Popup { +MainPopupBase { id: root - x: 50 - y: 25 - width: parent.width-100 - height: parent.height-50 - padding: 0 - closePolicy: Popup.CloseOnEscape + property string categorySelected : "" property alias oslist: oslist property alias osswipeview: osswipeview - // background of title - Rectangle { - id: ospopup_title_background - color: "#f5f5f5" - anchors.left: parent.left - anchors.top: parent.top - height: 35 - width: parent.width - - Text { - text: qsTr("Operating System") - horizontalAlignment: Text.AlignHCenter - anchors.fill: parent - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - } + title: qsTr("Operating System") - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - root.close() - osswipeview.decrementCurrentIndex() - } - } - } - } - // line under title - Rectangle { - id: ospopup_title_separator - color: "#afafaf" - width: parent.width - anchors.top: ospopup_title_background.bottom - height: 1 + onClosed: { + osswipeview.decrementCurrentIndex() } SwipeView { - anchors.top: ospopup_title_separator.bottom + anchors.top: root.title_separator.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom From 39a12071891af40a644dd82fe8cd0a37f934a92b Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 13:41:03 +0000 Subject: [PATCH 16/47] fix unqualified usage of hostname field --- src/OptionsPopup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index 4f112c56..7b930bc0 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -180,7 +180,7 @@ Window { hasSavedSettings = true } if ('hostname' in settings) { - fieldHostname.text = settings.hostname + generalTab.fieldHostname.text = settings.hostname generalTab.chkHostname.checked = true } if ('sshUserPassword' in settings) { From 2f0ec8b407f1138a84e0939b4af675cd271d65ae Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 14:42:58 +0000 Subject: [PATCH 17/47] Share code between the main popup list views --- src/CMakeLists.txt | 1 + src/DstPopup.qml | 16 ++-------------- src/HwPopup.qml | 18 ++++-------------- src/MainPopupListViewBase.qml | 25 +++++++++++++++++++++++++ src/OSPopup.qml | 15 +++------------ 5 files changed, 35 insertions(+), 40 deletions(-) create mode 100644 src/MainPopupListViewBase.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1784cda3..25efa358 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -332,6 +332,7 @@ set(IMAGER_QML_FILES HwPopup.qml OSPopup.qml DstPopup.qml + MainPopupListViewBase.qml ) set_source_files_properties(Style.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) diff --git a/src/DstPopup.qml b/src/DstPopup.qml index e920dac2..75b58020 100644 --- a/src/DstPopup.qml +++ b/src/DstPopup.qml @@ -18,18 +18,12 @@ MainPopupBase { title: qsTr("Storage") - ListView { + MainPopupListViewBase { id: dstlist model: window.driveListModel delegate: dstdelegate - anchors.top: root.title_separator.bottom - anchors.left: parent.left anchors.right: parent.right - anchors.bottom: filterRow.top - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - clip: true Label { anchors.fill: parent @@ -40,11 +34,6 @@ MainPopupBase { font.bold: true } - ScrollBar.vertical: ScrollBar { - width: 10 - policy: dstlist.contentHeight > dstlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } - Keys.onSpacePressed: { if (currentIndex == -1) return @@ -55,9 +44,8 @@ MainPopupBase { return selectDstItem(currentItem) } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) } + RowLayout { id: filterRow anchors { diff --git a/src/HwPopup.qml b/src/HwPopup.qml index 26e8a316..7840091a 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -19,9 +19,10 @@ MainPopupBase { required property SwipeView osswipeview title: qsTr("Raspberry Pi Device") - ListView { + MainPopupListViewBase { id: hwlist - clip: true + anchors.right: parent.right + model: ListModel { id: deviceModel ListElement { @@ -35,16 +36,7 @@ MainPopupBase { currentIndex: -1 delegate: hwdelegate anchors.top: root.title_separator.bottom - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - ScrollBar.vertical: ScrollBar { - anchors.right: parent.right - width: 10 - policy: hwlist.contentHeight > hwlist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } + Keys.onSpacePressed: { if (currentIndex != -1) root.selectHWitem(model.get(currentIndex)) @@ -53,8 +45,6 @@ MainPopupBase { if (currentIndex != -1) root.selectHWitem(model.get(currentIndex)) } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) } Component { diff --git a/src/MainPopupListViewBase.qml b/src/MainPopupListViewBase.qml new file mode 100644 index 00000000..33247a8f --- /dev/null +++ b/src/MainPopupListViewBase.qml @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2021 Raspberry Pi Ltd + */ + +import QtQuick +import QtQuick.Controls + +ListView { + id: root + clip: true + + anchors.left: parent.left + anchors.bottom: parent.bottom + boundsBehavior: Flickable.StopAtBounds + highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + + ScrollBar.vertical: ScrollBar { + width: 10 + policy: root.contentHeight > root.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + } + + Keys.onEnterPressed: (event) => { Keys.spacePressed(event) } + Keys.onReturnPressed: (event) => { Keys.spacePressed(event) } +} diff --git a/src/OSPopup.qml b/src/OSPopup.qml index c1aaf28f..8bc12611 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -31,22 +31,14 @@ MainPopupBase { interactive: false clip: true - ListView { + MainPopupListViewBase { id: oslist model: osmodel currentIndex: -1 delegate: osdelegate anchors.top: parent.top - anchors.left: parent.left - anchors.bottom: parent.bottom width: root.width - boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } - ScrollBar.vertical: ScrollBar { - anchors.right: parent.right - width: 10 - policy: oslist.contentHeight > oslist.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded - } + Keys.onSpacePressed: { if (currentIndex != -1) root.selectOSitem(model.get(currentIndex), true) @@ -55,8 +47,7 @@ MainPopupBase { if (currentIndex != -1) root.selectOSitem(model.get(currentIndex), true) } - Keys.onEnterPressed: (event) => { Keys.spacePressed(event) } - Keys.onReturnPressed: (event) => { Keys.spacePressed(event) } + Keys.onRightPressed: { // Navigate into sublists but don't select an OS entry if (currentIndex != -1 && root.isOSsublist(model.get(currentIndex))) From 81aab860a0bf16c0cd546250e6494a72ce25cac2 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 20:00:00 +0000 Subject: [PATCH 18/47] Fix qmllint warnings --- src/DstPopup.qml | 48 ++++++++----- src/HwPopup.qml | 36 +++++++--- src/MainPopupBase.qml | 1 + src/OSPopup.qml | 153 ++++++++++++++++++++++++++++++++---------- src/main.qml | 77 ++++----------------- 5 files changed, 191 insertions(+), 124 deletions(-) diff --git a/src/DstPopup.qml b/src/DstPopup.qml index 75b58020..6ff198f8 100644 --- a/src/DstPopup.qml +++ b/src/DstPopup.qml @@ -3,24 +3,30 @@ * Copyright (C) 2021 Raspberry Pi Ltd */ +pragma ComponentBehavior: Bound + import QtQuick 2.15 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 +import RpiImager + MainPopupBase { id: root - onClosed: window.imageWriter.stopDriveListPolling() - + required property ImageWriter imageWriter + required property DriveListModel driveListModel property alias dstlist: dstlist + onClosed: imageWriter.stopDriveListPolling() + title: qsTr("Storage") MainPopupListViewBase { id: dstlist - model: window.driveListModel + model: root.driveListModel delegate: dstdelegate anchors.top: root.title_separator.bottom anchors.right: parent.right @@ -37,12 +43,12 @@ MainPopupBase { Keys.onSpacePressed: { if (currentIndex == -1) return - selectDstItem(currentItem) + root.selectDstItem(currentItem) } Accessible.onPressAction: { if (currentIndex == -1) return - selectDstItem(currentItem) + root.selectDstItem(currentItem) } } @@ -68,6 +74,19 @@ MainPopupBase { Item { id: dstitem + + required property string device + required property string description + required property string size + required property bool isUsb + required property bool isScsi + required property bool isReadOnly + required property bool isSystem + required property var mountpoints + required property QtObject modelData + + readonly property bool unselectable: (isSystem && filterSystemDrives.checked) || isReadOnly + anchors.left: parent.left anchors.right: parent.right Layout.topMargin: 1 @@ -79,10 +98,7 @@ MainPopupBase { } return txt; } - property string description: model.description - property string device: model.device - property string size: model.size - property bool unselectable: (isSystem && filterSystemDrives.checked) || isReadOnly + Rectangle { id: dstbgrect @@ -103,7 +119,7 @@ MainPopupBase { Image { id: dstitem_image - source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg" + source: dstitem.isUsb ? "icons/ic_usb_40px.svg" : dstitem.isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg" verticalAlignment: Image.AlignVCenter fillMode: Image.Pad } @@ -121,8 +137,8 @@ MainPopupBase { font.pointSize: 16 color: !dstitem.unselectable ? "" : "grey"; text: { - var sizeStr = (size/1000000000).toFixed(1)+ " " + qsTr("GB"); - return description + " - " + sizeStr; + var sizeStr = (dstitem.size/1000000000).toFixed(1)+ " " + qsTr("GB"); + return dstitem.description + " - " + sizeStr; } } @@ -134,10 +150,10 @@ MainPopupBase { font.pointSize: 12 color: !dstitem.unselectable ? "" : "grey"; text: { - var txt= qsTr("Mounted as %1").arg(mountpoints.join(", ")); - if (isReadOnly) { + var txt= qsTr("Mounted as %1").arg(dstitem.mountpoints.join(", ")); + if (dstitem.isReadOnly) { txt += " " + qsTr("[WRITE PROTECTED]"); - } else if (isSystem) { + } else if (dstitem.isSystem) { text += " [" + qsTr("SYSTEM") + "]"; } return txt; @@ -171,7 +187,7 @@ MainPopupBase { } onClicked: { - selectDstItem(model) + root.selectDstItem(dstitem.modelData) } } } diff --git a/src/HwPopup.qml b/src/HwPopup.qml index 7840091a..fabfc5bc 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -3,12 +3,16 @@ * Copyright (C) 2021 Raspberry Pi Ltd */ +pragma ComponentBehavior: Bound + import QtQuick 2.15 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 +import RpiImager + MainPopupBase { id: root @@ -17,6 +21,8 @@ MainPopupBase { property alias hwlist: hwlist required property ListView oslist required property SwipeView osswipeview + required property ListModel osmodel + title: qsTr("Raspberry Pi Device") MainPopupListViewBase { @@ -51,7 +57,17 @@ MainPopupBase { id: hwdelegate Item { - width: window.width-100 + id: delegateRoot + required property int index + required property string name + required property string description + required property string icon + required property QtObject modelData + + // The code bellow mentions tooltip, but model doesn't have it + readonly property string tooltip: "" + + width: root.windowWidth - 100 height: contentLayout.implicitHeight + 24 Accessible.name: name+".\n"+description @@ -70,7 +86,7 @@ MainPopupBase { } onClicked: { - selectHWitem(model) + root.selectHWitem(delegateRoot.modelData) } } @@ -78,7 +94,7 @@ MainPopupBase { id: bgrect anchors.fill: parent color: "#f5f5f5" - visible: mouseOver && parent.ListView.view.currentIndex !== index + visible: mouseOver && parent.ListView.view.currentIndex !== delegateRoot.index property bool mouseOver: false } Rectangle { @@ -100,7 +116,7 @@ MainPopupBase { spacing: 12 Image { - source: typeof icon === "undefined" ? "" : icon + source: typeof icon === "undefined" ? "" : delegateRoot.icon Layout.preferredHeight: 64 Layout.preferredWidth: 64 sourceSize.width: 64 @@ -113,7 +129,7 @@ MainPopupBase { Layout.fillWidth: true Text { - text: name + text: delegateRoot.name elide: Text.ElideRight font.family: Style.fontFamily font.bold: true @@ -122,15 +138,15 @@ MainPopupBase { Text { Layout.fillWidth: true font.family: Style.fontFamily - text: description + text: delegateRoot.description wrapMode: Text.WordWrap color: "#1a1a1a" } ToolTip { - visible: hwMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" + visible: hwMouseArea.containsMouse && typeof(tooltip) == "string" && delegateRoot.tooltip != "" delay: 1000 - text: typeof(tooltip) == "string" ? tooltip : "" + text: typeof(tooltip) == "string" ? delegateRoot.tooltip : "" clip: false } } @@ -178,9 +194,9 @@ MainPopupBase { } } - osmodel.clear() + root.osmodel.clear() for (var i in oslist_parsed) { - osmodel.append(oslist_parsed[i]) + root.osmodel.append(oslist_parsed[i]) } // When the HW device is changed, reset the OS selection otherwise diff --git a/src/MainPopupBase.qml b/src/MainPopupBase.qml index dd29688c..dddf8377 100644 --- a/src/MainPopupBase.qml +++ b/src/MainPopupBase.qml @@ -16,6 +16,7 @@ Popup { padding: 0 closePolicy: Popup.CloseOnEscape + required property int windowWidth required property string title property alias title_separator: title_separator diff --git a/src/OSPopup.qml b/src/OSPopup.qml index 8bc12611..398eb2f5 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -3,6 +3,8 @@ * Copyright (C) 2021 Raspberry Pi Ltd */ +pragma ComponentBehavior: Bound + import QtQuick 2.15 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 @@ -15,6 +17,9 @@ MainPopupBase { property string categorySelected : "" property alias oslist: oslist property alias osswipeview: osswipeview + property alias osmodel: osmodel + + required property ImageWriter imageWriter title: qsTr("Operating System") @@ -56,10 +61,21 @@ MainPopupBase { } } + ListModel { + id: osmodel + + Component.onCompleted: { + if (root.imageWriter.isOnline()) { + root.fetchOSlist(); + } + } + } + Component { id: suboslist ListView { + id: sublistview model: ListModel { ListElement { url: "" @@ -86,26 +102,26 @@ MainPopupBase { highlight: Rectangle { color: "lightsteelblue"; radius: 5 } ScrollBar.vertical: ScrollBar { width: 10 - policy: parent.contentHeight > parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded + policy: sublistview.contentHeight > parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded } Keys.onSpacePressed: { if (currentIndex != -1) - selectOSitem(model.get(currentIndex)) + root.selectOSitem(model.get(currentIndex)) } Accessible.onPressAction: { if (currentIndex != -1) - selectOSitem(model.get(currentIndex)) + root.selectOSitem(model.get(currentIndex)) } - Keys.onEnterPressed: Keys.onSpacePressed(event) - Keys.onReturnPressed: Keys.onSpacePressed(event) + Keys.onEnterPressed: (event) => { Keys.spacePressed(event) } + Keys.onReturnPressed: (event) => { Keys.spacePressed(event) } Keys.onRightPressed: { // Navigate into sublists but don't select an OS entry - if (currentIndex != -1 && isOSsublist(model.get(currentIndex))) - selectOSitem(model.get(currentIndex), true) + if (currentIndex != -1 && root.isOSsublist(model.get(currentIndex))) + root.selectOSitem(model.get(currentIndex), true) } Keys.onLeftPressed: { osswipeview.decrementCurrentIndex() - ospopup.categorySelected = "" + root.categorySelected = "" } } } @@ -114,7 +130,23 @@ MainPopupBase { id: osdelegate Item { - width: window.width-100 + id: delegateItem + required property int index + required property string name + required property string description + required property string icon + required property string release_date + required property string url + required property string subitems_json + required property string extract_sha256 + required property QtObject model + required property int image_download_size // KDAB + + property string website + property string tooltip + property string subitems_url + + width: root.windowWidth - 100 height: contentLayout.implicitHeight + 24 Accessible.name: name+".\n"+description @@ -133,7 +165,7 @@ MainPopupBase { } onClicked: { - selectOSitem(model) + root.selectOSitem(delegateItem.model) } } @@ -141,7 +173,7 @@ MainPopupBase { id: bgrect anchors.fill: parent color: "#f5f5f5" - visible: mouseOver && parent.ListView.view.currentIndex !== index + visible: mouseOver && parent.ListView.view.currentIndex !== delegateItem.index property bool mouseOver: false } Rectangle { @@ -163,7 +195,7 @@ MainPopupBase { spacing: 12 Image { - source: icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png": icon + source: delegateItem.icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png": delegateItem.icon Layout.preferredHeight: 40 Layout.preferredWidth: 40 sourceSize.width: 40 @@ -178,7 +210,7 @@ MainPopupBase { RowLayout { spacing: 12 Text { - text: name + text: delegateItem.name elide: Text.ElideRight font.family: Style.fontFamily font.bold: true @@ -187,10 +219,10 @@ MainPopupBase { source: "icons/ic_info_16px.png" Layout.preferredHeight: 16 Layout.preferredWidth: 16 - visible: typeof(website) == "string" && website + visible: typeof(website) == "string" && delegateItem.website MouseArea { anchors.fill: parent - onClicked: Qt.openUrlExternally(website) + onClicked: Qt.openUrlExternally(delegateItem.website) } } Item { @@ -201,7 +233,7 @@ MainPopupBase { Text { Layout.fillWidth: true font.family: Style.fontFamily - text: description + text: delegateItem.description wrapMode: Text.WordWrap color: "#1a1a1a" } @@ -211,33 +243,33 @@ MainPopupBase { elide: Text.ElideRight color: "#646464" font.weight: Font.Light - visible: typeof(release_date) == "string" && release_date - text: qsTr("Released: %1").arg(release_date) + visible: typeof(delegateItem.release_date) == "string" && delegateItem.release_date + text: qsTr("Released: %1").arg(delegateItem.release_date) } Text { Layout.fillWidth: true elide: Text.ElideRight color: "#646464" font.weight: Font.Light - visible: typeof(url) == "string" && url != "" && url != "internal://format" - text: !url ? "" : - typeof(extract_sha256) != "undefined" && imageWriter.isCached(url,extract_sha256) + visible: typeof(delegateItem.url) == "string" && delegateItem.url != "" && delegateItem.url != "internal://format" + text: !delegateItem.url ? "" : + typeof(delegateItem.extract_sha256) != "undefined" && root.imageWriter.isCached(delegateItem.url, delegateItem.extract_sha256) ? qsTr("Cached on your computer") - : url.startsWith("file://") + : delegateItem.url.startsWith("file://") ? qsTr("Local file") - : qsTr("Online - %1 GB download").arg((image_download_size/1073741824).toFixed(1)) + : qsTr("Online - %1 GB download").arg((delegateItem.image_download_size/1073741824).toFixed(1)) } ToolTip { - visible: osMouseArea.containsMouse && typeof(tooltip) == "string" && tooltip != "" + visible: osMouseArea.containsMouse && typeof(delegateItem.tooltip) == "string" && delegateItem.tooltip != "" delay: 1000 - text: typeof(tooltip) == "string" ? tooltip : "" + text: typeof(delegateItem.tooltip) == "string" ? delegateItem.tooltip : "" clip: false } } Image { source: "icons/ic_chevron_right_40px.svg" - visible: (typeof(subitems_json) == "string" && subitems_json != "") || (typeof(subitems_url) == "string" && subitems_url != "" && subitems_url != "internal://back") + visible: (typeof(delegateItem.subitems_json) == "string" && delegateItem.subitems_json != "") || (typeof(delegateItem.subitems_url) == "string" && delegateItem.subitems_url != "" && delegateItem.subitems_url != "internal://back") Layout.preferredHeight: 40 Layout.preferredWidth: 40 fillMode: Image.PreserveAspectFit @@ -280,12 +312,12 @@ MainPopupBase { osswipeview.itemAt(osswipeview.currentIndex+1).currentIndex = (selectFirstSubitem === true) ? 0 : -1 osswipeview.incrementCurrentIndex() - ospopup.categorySelected = d.name + root.categorySelected = d.name } else if (typeof(d.subitems_url) == "string" && d.subitems_url !== "") { if (d.subitems_url === "internal://back") { osswipeview.decrementCurrentIndex() - ospopup.categorySelected = "" + root.categorySelected = "" } else { @@ -295,14 +327,14 @@ MainPopupBase { osswipeview.incrementCurrentIndex() } } else if (d.url === "") { - if (!imageWriter.isEmbeddedMode()) { - imageWriter.openFileDialog() + if (!root.imageWriter.isEmbeddedMode()) { + root.imageWriter.openFileDialog() } else { - if (imageWriter.mountUsbSourceMedia()) { + if (root.imageWriter.mountUsbSourceMedia()) { var m = newSublist() - var usboslist = JSON.parse(imageWriter.getUsbSourceOSlist()) + var usboslist = JSON.parse(root.imageWriter.getUsbSourceOSlist()) for (var i in usboslist) { m.append(usboslist[i]) } @@ -315,11 +347,11 @@ MainPopupBase { } } } else { - imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, ospopup.categorySelected, d.name, typeof(d.init_format) != "undefined" ? d.init_format : "") + root.imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, root.categorySelected, d.name, typeof(d.init_format) != "undefined" ? d.init_format : "") osbutton.text = d.name - ospopup.close() + root.close() osswipeview.decrementCurrentIndex() - if (imageWriter.readyToWrite()) { + if (root.imageWriter.readyToWrite()) { writebutton.enabled = true } } @@ -358,4 +390,55 @@ MainPopupBase { return false } + + function fetchOSlist() { + var oslist_json = root.imageWriter.getFilteredOSlist(); + var o = JSON.parse(oslist_json) + var oslist_parsed = oslistFromJson(o) + if (oslist_parsed === false) + return + osmodel.clear() + for (var i in oslist_parsed) { + osmodel.append(oslist_parsed[i]) + } + + if ("imager" in o) { + var imager = o["imager"] + + if ("devices" in imager) + { + hwpopup.deviceModel.clear() + var devices = imager["devices"] + for (var j in devices) + { + devices[j]["tags"] = JSON.stringify(devices[j]["tags"]) + hwpopup.deviceModel.append(devices[j]) + if ("default" in devices[j] && devices[j]["default"]) + { + hwpopup.hwlist.currentIndex = hwpopup.deviceModel.count-1 + } + } + } + + if (root.imageWriter.getBoolSetting("check_version") && "latest_version" in imager && "url" in imager) { + if (!root.imageWriter.isEmbeddedMode() && root.imageWriter.isVersionNewer(imager["latest_version"])) { + updatepopup.url = imager["url"] + updatepopup.openPopup() + } + } + if ("default_os" in imager) { + selectNamedOS(imager["default_os"], osmodel) + } + if (root.imageWriter.isEmbeddedMode()) { + if ("embedded_default_os" in imager) { + selectNamedOS(imager["embedded_default_os"], osmodel) + } + if ("embedded_default_destination" in imager) { + root.imageWriter.startDriveListPolling() + setDefaultDest.drive = imager["embedded_default_destination"] + setDefaultDest.start() + } + } + } + } } diff --git a/src/main.qml b/src/main.qml index 86ed5dac..0a664bd8 100644 --- a/src/main.qml +++ b/src/main.qml @@ -433,25 +433,22 @@ ApplicationWindow { HwPopup { id: hwpopup oslist: ospopup.oslist + osmodel: ospopup.osmodel osswipeview: ospopup.osswipeview + windowWidth: window.width } OSPopup { id: ospopup - } - - ListModel { - id: osmodel - - Component.onCompleted: { - if (imageWriter.isOnline()) { - fetchOSlist(); - } - } + windowWidth: window.width + imageWriter: window.imageWriter } DstPopup { id: dstpopup + imageWriter: window.imageWriter + driveListModel: window.driveListModel + windowWidth: window.width } MsgPopup { @@ -527,7 +524,7 @@ ApplicationWindow { minimumWidth: 450 minimumHeight: 400 id: optionspopup - onSaveSettingsSignal: { + onSaveSettingsSignal: (settings) => { window.imageWriter.setSavedCustomizationSettings(settings) usesavedsettingspopup.hasSavedSettings = true } @@ -604,7 +601,7 @@ ApplicationWindow { } function onOsListPrepared() { - fetchOSlist() + ospopup.fetchOSlist() } function resetWriteButton() { @@ -811,57 +808,6 @@ ApplicationWindow { return oslist_parsed } - function fetchOSlist() { - var oslist_json = imageWriter.getFilteredOSlist(); - var o = JSON.parse(oslist_json) - var oslist_parsed = oslistFromJson(o) - if (oslist_parsed === false) - return - osmodel.clear() - for (var i in oslist_parsed) { - osmodel.append(oslist_parsed[i]) - } - - if ("imager" in o) { - var imager = o["imager"] - - if ("devices" in imager) - { - hwpopup.deviceModel.clear() - var devices = imager["devices"] - for (var j in devices) - { - devices[j]["tags"] = JSON.stringify(devices[j]["tags"]) - hwpopup.deviceModel.append(devices[j]) - if ("default" in devices[j] && devices[j]["default"]) - { - hwpopup.hwlist.currentIndex = hwpopup.deviceModel.count-1 - } - } - } - - if (imageWriter.getBoolSetting("check_version") && "latest_version" in imager && "url" in imager) { - if (!imageWriter.isEmbeddedMode() && imageWriter.isVersionNewer(imager["latest_version"])) { - updatepopup.url = imager["url"] - updatepopup.openPopup() - } - } - if ("default_os" in imager) { - selectNamedOS(imager["default_os"], osmodel) - } - if (imageWriter.isEmbeddedMode()) { - if ("embedded_default_os" in imager) { - selectNamedOS(imager["embedded_default_os"], osmodel) - } - if ("embedded_default_destination" in imager) { - imageWriter.startDriveListPolling() - setDefaultDest.drive = imager["embedded_default_destination"] - setDefaultDest.start() - } - } - } - } - Timer { /* Verify if default drive is in our list after 100 ms */ id: setDefaultDest @@ -884,4 +830,9 @@ ApplicationWindow { } } } + + // Called from C++ + function fetchOSlist() { + ospopup.fetchOSlist() + } } From 161337341598b7dc83e59fb40e458e8bfb11af54 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 20:00:17 +0000 Subject: [PATCH 19/47] Fix vscode (or other qmlls clients) Setting QT_QML_GENERATE_QMLLS_INI creates an ini file inside the source directory, which point to the module's location --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25efa358..9fec4b37 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -313,6 +313,8 @@ if (QT_KNOWN_POLICY_QTP0004) qt_policy(SET QTP0004 NEW) endif() +set(QT_QML_GENERATE_QMLLS_INI ON) + set(IMAGER_QML_FILES main.qml MsgPopup.qml From 1dcc8e706ef4e4ecf77491fc6b867b4117041bc2 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 6 Feb 2025 20:04:50 +0000 Subject: [PATCH 20/47] fix DnD errors and warnings The warning was: main.qml:425:17 Parameter "drop" is not declared. Injection of parameters into signal handlers is deprecated. Use JavaScript functions with formal parameters instead. The error was about mimedata being undefined, caught by qmllint --- src/main.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.qml b/src/main.qml index 0a664bd8..7221ce41 100644 --- a/src/main.qml +++ b/src/main.qml @@ -416,14 +416,14 @@ ApplicationWindow { DropArea { anchors.fill: parent - onEntered: { - if (drag.active && mimeData.hasUrls()) { + onEntered: (drag) => { + if (Drag.active && drag.hasUrls) { drag.acceptProposedAction() } } - onDropped: { + onDropped: (drop) => { if (drop.urls && drop.urls.length > 0) { - onFileSelected(drop.urls[0].toString()) + window.onFileSelected(drop.urls[0].toString()) } } } From 0e1a21fcdab2cc485e4295fffdeb93c290d69193 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 7 Feb 2025 15:45:33 +0000 Subject: [PATCH 21/47] Fix remaining qmllint warnings --- src/MsgPopup.qml | 2 ++ src/OSPopup.qml | 6 ++++-- src/OptionsGeneralTab.qml | 2 ++ src/OptionsMiscTab.qml | 2 ++ src/OptionsPopup.qml | 7 ++++++- src/OptionsServicesTab.qml | 28 ++++++++++++++++++---------- src/OptionsTabBase.qml | 2 ++ src/UseSavedSettingsPopup.qml | 3 +++ src/main.qml | 7 ++++++- src/qmlcomponents/ImButton.qml | 2 ++ src/qmlcomponents/ImPopup.qml | 2 ++ 11 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/MsgPopup.qml b/src/MsgPopup.qml index c468cb66..b737e70d 100644 --- a/src/MsgPopup.qml +++ b/src/MsgPopup.qml @@ -9,6 +9,8 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 import "qmlcomponents" +import RpiImager + ImPopup { id: root closePolicy: Popup.CloseOnEscape diff --git a/src/OSPopup.qml b/src/OSPopup.qml index 398eb2f5..a06b0d9b 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -11,6 +11,8 @@ import QtQuick.Layouts 1.15 import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 +import RpiImager + MainPopupBase { id: root @@ -364,7 +366,7 @@ MainPopupBase { osswipeview.addItem(newlist) } - var m = osswipeview.itemAt(osswipeview.currentIndex+1).model + var m = osswipeview.itemAt(osswipeview.currentIndex+1).model // qmllint disable missing-property if (m.count>1) { @@ -394,7 +396,7 @@ MainPopupBase { function fetchOSlist() { var oslist_json = root.imageWriter.getFilteredOSlist(); var o = JSON.parse(oslist_json) - var oslist_parsed = oslistFromJson(o) + var oslist_parsed = oslistFromJson(o) // qmllint disable unqualified if (oslist_parsed === false) return osmodel.clear() diff --git a/src/OptionsGeneralTab.qml b/src/OptionsGeneralTab.qml index d5d3227b..d45df66e 100644 --- a/src/OptionsGeneralTab.qml +++ b/src/OptionsGeneralTab.qml @@ -10,6 +10,8 @@ import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 import "qmlcomponents" +import RpiImager + OptionsTabBase { id: root diff --git a/src/OptionsMiscTab.qml b/src/OptionsMiscTab.qml index cd6a7628..4cc8f67a 100644 --- a/src/OptionsMiscTab.qml +++ b/src/OptionsMiscTab.qml @@ -8,6 +8,8 @@ import QtQuick.Layouts 1.15 import QtQuick.Window 2.15 import "qmlcomponents" +import RpiImager + OptionsTabBase { id: root diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index 7b930bc0..d7f49efc 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -10,6 +10,8 @@ import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 import "qmlcomponents" +import RpiImager + Window { id: popup width: 550 @@ -27,6 +29,8 @@ Window { modality: Qt.WindowModal + required property ImageWriter imageWriter + property bool initialized: false property bool hasSavedSettings: false property string config @@ -95,6 +99,7 @@ Window { OptionsServicesTab { id: remoteAccessTab + imageWriter: popup.imageWriter chkSetUser: generalTab.chkSetUser fieldUserName: generalTab.fieldUserName fieldUserPassword: generalTab.fieldUserPassword @@ -615,7 +620,7 @@ Window { generalTab.fieldKeyboardLayout.model = imageWriter.getKeymapLayoutList() generalTab.fieldWifiCountry.model = imageWriter.getCountryList() - fieldHostname.text = "raspberrypi" + generalTab.fieldHostname.text = "raspberrypi" generalTab.fieldUserName.text = imageWriter.getCurrentUser() generalTab.fieldUserPassword.clear() remoteAccessTab.radioPubKeyAuthentication.checked = false diff --git a/src/OptionsServicesTab.qml b/src/OptionsServicesTab.qml index 7457b258..92f63db0 100644 --- a/src/OptionsServicesTab.qml +++ b/src/OptionsServicesTab.qml @@ -3,6 +3,8 @@ * Copyright (C) 2021 Raspberry Pi Ltd */ +pragma ComponentBehavior: Bound + import QtQuick 2.15 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 @@ -10,6 +12,8 @@ import QtQuick.Controls.Material 2.2 import QtQuick.Window 2.15 import "qmlcomponents" +import RpiImager + OptionsTabBase { id: root @@ -19,6 +23,7 @@ OptionsTabBase { property alias radioPasswordAuthentication: radioPasswordAuthentication property alias radioPubKeyAuthentication: radioPubKeyAuthentication + required property ImageWriter imageWriter required property ImCheckBox chkSetUser required property TextField fieldUserName required property TextField fieldUserPassword @@ -77,7 +82,7 @@ OptionsTabBase { Layout.rightMargin: 5 Layout.minimumHeight: 50 Layout.fillHeight: true - Layout.preferredWidth: popup.width - (20 + Layout.leftMargin + Layout.rightMargin) + Layout.preferredWidth: root.width - (20 + Layout.leftMargin + Layout.rightMargin) ListView { id: publicKeyList @@ -92,33 +97,36 @@ OptionsTabBase { delegate: RowLayout { id: publicKeyItem - property int publicKeyModelIndex: index + required property int index + readonly property int publicKeyModelIndex: index + required property string publicKeyField + height: 50 TextField { id: contentField enabled: radioPubKeyAuthentication.checked validator: RegularExpressionValidator { regularExpression: /^ssh-(ed25519|rsa|dss|ecdsa) AAAA(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})( [A-Za-z0-9-\\\/]+@[A-Za-z0-9-\\\/]+)?/ } - text: model.publicKeyField + text: publicKeyItem.publicKeyField Layout.fillWidth: true Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter implicitWidth: publicKeyList.width - (removePublicKeyItem.width + 20) onEditingFinished: { - publicKeyModel.set(publicKeyModelIndex, {publicKeyField: contentField.text}) + publicKeyModel.set(publicKeyItem.publicKeyModelIndex, {publicKeyField: contentField.text}) } } ImButton { id: removePublicKeyItem text: qsTr("Delete Key") - enabled: radioPubKeyAuthentication.checked + enabled: root.radioPubKeyAuthentication.checked Layout.minimumWidth: 100 Layout.preferredWidth: 100 Layout.alignment: Qt.AlignRight | Qt.AlignVCenter onClicked: { - if (publicKeyModelIndex != -1) { - publicKeyModel.remove(publicKeyModelIndex) + if (publicKeyItem.publicKeyModelIndex != -1) { + publicKeyModel.remove(publicKeyItem.publicKeyModelIndex) publicKeyListViewContainer.implicitHeight -= 50 + publicKeyList.spacing } } @@ -131,11 +139,11 @@ OptionsTabBase { ImButton { text: qsTr("RUN SSH-KEYGEN") Layout.leftMargin: 40 - enabled: imageWriter.hasSshKeyGen() && !imageWriter.hasPubKey() + enabled: root.imageWriter.hasSshKeyGen() && !root.imageWriter.hasPubKey() onClicked: { enabled = false - imageWriter.generatePubKey() - publicKeyModel.append({publicKeyField: imageWriter.getDefaultPubKey()}) + root.imageWriter.generatePubKey() + publicKeyModel.append({publicKeyField: root.imageWriter.getDefaultPubKey()}) } } ImButton { diff --git a/src/OptionsTabBase.qml b/src/OptionsTabBase.qml index 5f959cb7..632b8306 100644 --- a/src/OptionsTabBase.qml +++ b/src/OptionsTabBase.qml @@ -7,6 +7,8 @@ import QtQuick 2.15 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.15 +import RpiImager + ScrollView { id: root diff --git a/src/UseSavedSettingsPopup.qml b/src/UseSavedSettingsPopup.qml index 404eafec..8ba4ce59 100644 --- a/src/UseSavedSettingsPopup.qml +++ b/src/UseSavedSettingsPopup.qml @@ -9,12 +9,15 @@ import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 import "qmlcomponents" +import RpiImager + ImPopup { id: root height: msgpopupbody.implicitHeight+150 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + required property ImageWriter imageWriter property bool hasSavedSettings: false signal noClearSettings() diff --git a/src/main.qml b/src/main.qml index 7221ce41..6c45a381 100644 --- a/src/main.qml +++ b/src/main.qml @@ -521,9 +521,12 @@ ApplicationWindow { } OptionsPopup { + id: optionspopup minimumWidth: 450 minimumHeight: 400 - id: optionspopup + + imageWriter: window.imageWriter + onSaveSettingsSignal: (settings) => { window.imageWriter.setSavedCustomizationSettings(settings) usesavedsettingspopup.hasSavedSettings = true @@ -532,6 +535,8 @@ ApplicationWindow { UseSavedSettingsPopup { id: usesavedsettingspopup + imageWriter: window.imageWriter + onYes: { optionspopup.initialize() optionspopup.applySettings() diff --git a/src/qmlcomponents/ImButton.qml b/src/qmlcomponents/ImButton.qml index 2b36b110..dd66fdab 100644 --- a/src/qmlcomponents/ImButton.qml +++ b/src/qmlcomponents/ImButton.qml @@ -7,6 +7,8 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Controls.Material 2.2 +import RpiImager + Button { font.family: Style.fontFamily font.capitalization: Font.AllUppercase diff --git a/src/qmlcomponents/ImPopup.qml b/src/qmlcomponents/ImPopup.qml index 7e7e4531..b86fe5f5 100644 --- a/src/qmlcomponents/ImPopup.qml +++ b/src/qmlcomponents/ImPopup.qml @@ -8,6 +8,8 @@ import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import QtQuick.Controls.Material 2.2 +import RpiImager + Popup { id: msgpopup x: (parent.width-width)/2 From 1d16bb97a55b63fad921d1fcc916fb04143c4feb Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 7 Feb 2025 15:46:29 +0000 Subject: [PATCH 22/47] update src/.gitignore CMakePresets create the build directory inside src, so ignore it here. Also ignore .qmlls.ini which is created by CMake now to help with IDE code model --- src/.gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/.gitignore b/src/.gitignore index 3587b720..6a8e8e20 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,3 +1,5 @@ - dependencies/libarchive-3.4.2/build/pkgconfig/libarchive.pc CMakeLists.txt.user +/build-* +.qmlls.ini + From fa88766a5981dd93fbbdcb9fe963cebba2ac642f Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 7 Feb 2025 15:47:55 +0000 Subject: [PATCH 23/47] qmllint: Bump all lints to fatal Since we've fixed them all we can mark them as fatal. Whenever someone introduces a problem, CI will yell at us. --- .qmllint.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.qmllint.ini b/.qmllint.ini index 84821442..17095101 100644 --- a/.qmllint.ini +++ b/.qmllint.ini @@ -24,7 +24,7 @@ InheritanceCycle=warning InvalidLintDirective=warning LayoutsPositioning=warning LintPluginWarnings=warning -MissingProperty=info +MissingProperty=warning MissingType=warning MultilineStrings=warning NonListProperty=warning @@ -40,7 +40,7 @@ TypeError=warning UncreatableType=warning UnexpectedVarType=warning UnknownProperty=warning -UnqualifiedAccess=info +UnqualifiedAccess=warning UnresolvedType=warning UnusedImports=warning UseProperFunction=warning From ff7d186766efd032f25f21f4eb2349f05c563bd0 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 13 Feb 2025 14:16:18 +0000 Subject: [PATCH 24/47] Fix OptionsPopup not honouring Esc key Keys.onEscapePressed only works for Item, which Window is not. --- src/OptionsPopup.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index d7f49efc..8a601073 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -43,8 +43,9 @@ Window { signal saveSettingsSignal(var settings) - Keys.onEscapePressed: { - popup.close() + Shortcut { + sequence: "Esc" + onActivated: popup.close() } ColumnLayout { From 2fd287f013387b3754dbadf322f3ff0b74f57fb2 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 14 Feb 2025 14:34:51 +0000 Subject: [PATCH 25/47] clazy: Fix -Wclazy-range-loop-detach Qt containers detach (copy on write) when a non-const method are called on them. Simply make the container const so the for loop calles cbegin() instead of begin(), which prevents detach. --- src/imagewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 91410576..2fca009b 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -592,7 +592,7 @@ namespace { // Filter this one! if (ositemObject.contains("devices")) { auto keep = false; - auto ositem_devices = ositemObject["devices"].toArray(); + const auto ositem_devices = ositemObject["devices"].toArray(); for (auto compat_device : ositem_devices) { if (hw_filter.contains(compat_device.toString())) { From 0574fb563af51160c0e48c53583e63358ee0ac22 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 14 Feb 2025 14:38:42 +0000 Subject: [PATCH 26/47] clazy: Fix clazy-use-static-qregularexpression QRegularExpression is expensive to create, so cache it --- src/downloadstatstelemetry.cpp | 2 +- src/imagewriter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/downloadstatstelemetry.cpp b/src/downloadstatstelemetry.cpp index d0ae00e6..49f6056a 100644 --- a/src/downloadstatstelemetry.cpp +++ b/src/downloadstatstelemetry.cpp @@ -32,7 +32,7 @@ DownloadStatsTelemetry::DownloadStatsTelemetry(const QByteArray &url, const QByt f.close(); if (cpuinfo.contains("Raspberry Pi")) { - QRegularExpression rx("Revision[ \t]*: ([0-9a-f]+)"); + static QRegularExpression rx("Revision[ \t]*: ([0-9a-f]+)"); QRegularExpressionMatch m = rx.match(cpuinfo); if (m.hasMatch()) { diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 2fca009b..84af1267 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -1392,7 +1392,7 @@ QString ImageWriter::detectPiKeyboard() if (!typenr) { QDir dir("/dev/input/by-id"); - QRegularExpression rx("RPI_Wired_Keyboard_([0-9]+)"); + static QRegularExpression rx("RPI_Wired_Keyboard_([0-9]+)"); const QStringList entries = dir.entryList(QDir::Files); for (const QString &fn : entries) From 3891b96bff64f4cdbc32301ef6ed9176654fe988 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 14 Feb 2025 14:43:52 +0000 Subject: [PATCH 27/47] clazy: Fix -Wclazy-detaching-temporary Use value() instead of operator[] as it doesn't detach the container --- src/imagewriter.cpp | 6 +++--- src/linux/linuxdrivelist.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 84af1267..d3e75517 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -623,14 +623,14 @@ QByteArray ImageWriter::getFilteredOSlist() { { if (!_completeOsList.isEmpty()) { if (!_deviceFilter.isEmpty()) { - reference_os_list_array = filterOsListWithHWTags(_completeOsList.object()["os_list"].toArray(), _deviceFilter, _deviceFilterIsInclusive); + reference_os_list_array = filterOsListWithHWTags(_completeOsList.object().value("os_list").toArray(), _deviceFilter, _deviceFilterIsInclusive); } else { // The device filter can be an empty array when a device filter has not been selected, or has explicitly been selected as // "no filtering". In that case, avoid walking the tree and use the unfiltered list. - reference_os_list_array = _completeOsList.object()["os_list"].toArray(); + reference_os_list_array = _completeOsList.object().value("os_list").toArray(); } - reference_imager_metadata = _completeOsList.object()["imager"].toObject(); + reference_imager_metadata = _completeOsList.object().value("imager").toObject(); } } diff --git a/src/linux/linuxdrivelist.cpp b/src/linux/linuxdrivelist.cpp index 2fcf8521..a4c8ac77 100644 --- a/src/linux/linuxdrivelist.cpp +++ b/src/linux/linuxdrivelist.cpp @@ -67,7 +67,7 @@ namespace Drivelist } QJsonDocument d = QJsonDocument::fromJson(output); - const QJsonArray a = d.object()["blockdevices"].toArray(); + const QJsonArray a = d.object().value("blockdevices").toArray(); for (const auto &i : a) { DeviceDescriptor d; From 5e5c8e7729c379be02dc0b65dd9d4cae1c583143 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 14 Feb 2025 14:47:54 +0000 Subject: [PATCH 28/47] clazy: Silence -Wclazy-lambda-in-connect Usually it's dangerous to capture local variables inside a connect, as the connect might call the lambda after those variables went out of scope. In this case it's fine as the connect is disconnected before we leave the function, as we wait for QProcess to end locally --- src/imagewriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index d3e75517..9c829780 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -106,7 +106,7 @@ ImageWriter::ImageWriter(QObject *parent) }; QProcess *findProcess = new QProcess(this); connect(findProcess, QOverload::of(&QProcess::finished), - [&blconfig_link, &findProcess](int exitCode, QProcess::ExitStatus exitStatus) { + [&blconfig_link, &findProcess](int exitCode, QProcess::ExitStatus exitStatus) { // clazy:exclude=lambda-in-connect blconfig_link = findProcess->readAllStandardOutput(); }); findProcess->start(); From f30cdad8b3fcc8eaa9f99e6da7914b78f76d177f Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 14 Feb 2025 14:59:05 +0000 Subject: [PATCH 29/47] clazy: fix -Wclazy-container-antipattern Don't allocate memory (due to temporary containers) when accessing data. values() is usually discouraged, it copies all the data internally into a QList. With an iterator we can access the data in place. --- src/drivelistmodel.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/drivelistmodel.cpp b/src/drivelistmodel.cpp index c37da53e..9742038c 100644 --- a/src/drivelistmodel.cpp +++ b/src/drivelistmodel.cpp @@ -46,8 +46,11 @@ QVariant DriveListModel::data(const QModelIndex &index, int role) const QByteArray propertyName = _rolenames.value(role); if (propertyName.isEmpty()) return QVariant(); - else - return _drivelist.values().at(row)->property(propertyName); + else { + auto it = _drivelist.cbegin(); + std::advance(it, row); + return it.value()->property(propertyName); + } } void DriveListModel::processDriveList(std::vector l) From 7929afae1db32f6800f5e839320ba77f463440d3 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 14 Feb 2025 15:12:29 +0000 Subject: [PATCH 30/47] clazy: Fix -Wclazy-no-module-include Includes like are module-wide includes, which include up to 50 other includes. Include only the specific headers we really need to avoid extra compiler work. --- src/downloadextractthread.cpp | 1 + src/downloadextractthread.h | 2 +- src/downloadthread.cpp | 3 ++- src/imagewriter.cpp | 4 +++- src/localfileextractthread.cpp | 2 ++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/downloadextractthread.cpp b/src/downloadextractthread.cpp index ac61df67..c5405360 100644 --- a/src/downloadextractthread.cpp +++ b/src/downloadextractthread.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using namespace std; diff --git a/src/downloadextractthread.h b/src/downloadextractthread.h index 002b9bf9..44303c39 100644 --- a/src/downloadextractthread.h +++ b/src/downloadextractthread.h @@ -9,7 +9,7 @@ #include "downloadthread.h" #include #include -#include +#include class _extractThreadClass; diff --git a/src/downloadthread.cpp b/src/downloadthread.cpp index 3db9c238..21b46e94 100644 --- a/src/downloadthread.cpp +++ b/src/downloadthread.cpp @@ -21,7 +21,8 @@ #include #include #include -#include +#include +#include #include #ifdef Q_OS_LINUX diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 9c829780..2b60144f 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -32,8 +32,10 @@ #include #include #include +#include +#include +#include #include -#include #ifndef QT_NO_WIDGETS #include #include diff --git a/src/localfileextractthread.cpp b/src/localfileextractthread.cpp index 4cef1678..f90046f8 100644 --- a/src/localfileextractthread.cpp +++ b/src/localfileextractthread.cpp @@ -6,6 +6,8 @@ #include "localfileextractthread.h" #include "config.h" +#include + LocalFileExtractThread::LocalFileExtractThread(const QByteArray &url, const QByteArray &dst, const QByteArray &expectedHash, QObject *parent) : DownloadExtractThread(url, dst, expectedHash, parent) { From 53aa65d1463c07f37395d4f58633cba2ec63687d Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Mon, 17 Feb 2025 10:33:09 +0000 Subject: [PATCH 31/47] Add an ImCloseButton This doesn't change the design of the old button, but removes the duplicate implementation. --- src/CMakeLists.txt | 1 + src/MainPopupBase.qml | 24 ++++--------------- src/qmlcomponents/ImCloseButton.qml | 37 +++++++++++++++++++++++++++++ src/qmlcomponents/ImPopup.qml | 22 +++-------------- 4 files changed, 46 insertions(+), 38 deletions(-) create mode 100644 src/qmlcomponents/ImCloseButton.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fec4b37..90f3af59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -328,6 +328,7 @@ set(IMAGER_QML_FILES qmlcomponents/ImButton.qml qmlcomponents/ImButtonRed.qml qmlcomponents/ImCheckBox.qml + qmlcomponents/ImCloseButton.qml qmlcomponents/ImPopup.qml qmlcomponents/ImRadioButton.qml MainPopupBase.qml diff --git a/src/MainPopupBase.qml b/src/MainPopupBase.qml index dddf8377..c8941604 100644 --- a/src/MainPopupBase.qml +++ b/src/MainPopupBase.qml @@ -3,10 +3,11 @@ * Copyright (C) 2025 Raspberry Pi Ltd */ import QtQuick -import QtQuick.Layouts import QtQuick.Controls import QtQuick.Controls.Material +import RpiImager + Popup { id: root x: 50 @@ -38,24 +39,9 @@ Popup { font.bold: true } - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - root.close() - } + ImCloseButton { + onClicked: { + root.close() } } } diff --git a/src/qmlcomponents/ImCloseButton.qml b/src/qmlcomponents/ImCloseButton.qml new file mode 100644 index 00000000..11e4928b --- /dev/null +++ b/src/qmlcomponents/ImCloseButton.qml @@ -0,0 +1,37 @@ + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +import QtQuick 2.9 +import QtQuick.Layouts 1.0 + +import RpiImager + +Text { + id: root + signal clicked + + text: "X" + + // Layouting is here as all usage sites use the same code, so we save some lines. + // Move it out usage once different usage is needed + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + verticalAlignment: Text.AlignVCenter + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + + font.family: Style.fontFamily + font.bold: true + + MouseArea { + anchors.fill: parent + onClicked: { + root.clicked(); + } + } +} diff --git a/src/qmlcomponents/ImPopup.qml b/src/qmlcomponents/ImPopup.qml index b86fe5f5..f9a4078c 100644 --- a/src/qmlcomponents/ImPopup.qml +++ b/src/qmlcomponents/ImPopup.qml @@ -44,25 +44,9 @@ Popup { font.bold: true } - Text { - text: "X" - Layout.alignment: Qt.AlignRight - horizontalAlignment: Text.AlignRight - verticalAlignment: Text.AlignVCenter - anchors.right: parent.right - anchors.top: parent.top - anchors.rightMargin: 25 - anchors.topMargin: 10 - font.family: Style.fontFamily - font.bold: true - - MouseArea { - anchors.fill: parent - // KDAB: This was not present in MessagePopup - cursorShape: Qt.PointingHandCursor - onClicked: { - msgpopup.close() - } + ImCloseButton { + onClicked: { + msgpopup.close(); } } } From bef7cc4d6e389897abc20f363fdc5eb38882d0a5 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Mon, 17 Feb 2025 11:56:08 +0000 Subject: [PATCH 32/47] Move hardcoded colors to Style.qml They're now in a central place where designers can conveniently change. Also reduces duplication, since many items now reuse the same style key. --- src/DstPopup.qml | 8 +++---- src/HwPopup.qml | 6 ++--- src/MainPopupBase.qml | 4 ++-- src/MainPopupListViewBase.qml | 4 +++- src/OSPopup.qml | 12 +++++----- src/OptionsGeneralTab.qml | 16 ++++++------- src/OptionsServicesTab.qml | 2 +- src/Style.qml | 40 +++++++++++++++++++++++++++++++ src/main.qml | 22 ++++++++--------- src/qmlcomponents/ImButton.qml | 4 ++-- src/qmlcomponents/ImButtonRed.qml | 6 +++-- src/qmlcomponents/ImPopup.qml | 4 ++-- 12 files changed, 86 insertions(+), 42 deletions(-) diff --git a/src/DstPopup.qml b/src/DstPopup.qml index 6ff198f8..dcff59e9 100644 --- a/src/DstPopup.qml +++ b/src/DstPopup.qml @@ -107,7 +107,7 @@ MainPopupBase { anchors.right: parent.right height: 60 - color: mouseOver ? "#f5f5f5" : "#ffffff" + color: mouseOver ? Style.listViewHoverRowBackgroundColor : Style.listViewRowBackgroundColor property bool mouseOver: false RowLayout { @@ -135,7 +135,7 @@ MainPopupBase { Layout.fillWidth: true font.family: Style.fontFamily font.pointSize: 16 - color: !dstitem.unselectable ? "" : "grey"; + color: !dstitem.unselectable ? "" : Style.formLabelDisabledColor; text: { var sizeStr = (dstitem.size/1000000000).toFixed(1)+ " " + qsTr("GB"); return dstitem.description + " - " + sizeStr; @@ -148,7 +148,7 @@ MainPopupBase { Layout.fillWidth: true font.family: Style.fontFamily font.pointSize: 12 - color: !dstitem.unselectable ? "" : "grey"; + color: !dstitem.unselectable ? "" : Style.formLabelDisabledColor; text: { var txt= qsTr("Mounted as %1").arg(dstitem.mountpoints.join(", ")); if (dstitem.isReadOnly) { @@ -169,7 +169,7 @@ MainPopupBase { anchors.left: parent.left anchors.right: parent.right height: 1 - color: "#dcdcdc" + color: Style.popupBorderColor } MouseArea { diff --git a/src/HwPopup.qml b/src/HwPopup.qml index fabfc5bc..ae1b8aea 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -93,7 +93,7 @@ MainPopupBase { Rectangle { id: bgrect anchors.fill: parent - color: "#f5f5f5" + color: Style.titleBackgroundColor visible: mouseOver && parent.ListView.view.currentIndex !== delegateRoot.index property bool mouseOver: false } @@ -101,7 +101,7 @@ MainPopupBase { id: borderrect implicitHeight: 1 implicitWidth: parent.width - color: "#dcdcdc" + color: Style.popupBorderColor y: parent.height } @@ -140,7 +140,7 @@ MainPopupBase { font.family: Style.fontFamily text: delegateRoot.description wrapMode: Text.WordWrap - color: "#1a1a1a" + color: Style.textDescriptionColor } ToolTip { diff --git a/src/MainPopupBase.qml b/src/MainPopupBase.qml index c8941604..b2928f7a 100644 --- a/src/MainPopupBase.qml +++ b/src/MainPopupBase.qml @@ -24,7 +24,7 @@ Popup { // background of title Rectangle { id: title_background - color: "#f5f5f5" + color: Style.titleBackgroundColor anchors.left: parent.left anchors.top: parent.top height: 35 @@ -48,7 +48,7 @@ Popup { // line under title Rectangle { id: title_separator - color: "#afafaf" + color: Style.titleSeparatorColor width: parent.width anchors.top: title_background.bottom height: 1 diff --git a/src/MainPopupListViewBase.qml b/src/MainPopupListViewBase.qml index 33247a8f..f1b9180c 100644 --- a/src/MainPopupListViewBase.qml +++ b/src/MainPopupListViewBase.qml @@ -6,6 +6,8 @@ import QtQuick import QtQuick.Controls +import RpiImager + ListView { id: root clip: true @@ -13,7 +15,7 @@ ListView { anchors.left: parent.left anchors.bottom: parent.bottom boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + highlight: Rectangle { color: Style.listViewHighlightColor; radius: 5 } ScrollBar.vertical: ScrollBar { width: 10 diff --git a/src/OSPopup.qml b/src/OSPopup.qml index a06b0d9b..ca29aab1 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -101,7 +101,7 @@ MainPopupBase { delegate: osdelegate boundsBehavior: Flickable.StopAtBounds - highlight: Rectangle { color: "lightsteelblue"; radius: 5 } + highlight: Rectangle { color: Style.listViewHighlightColor; radius: 5 } ScrollBar.vertical: ScrollBar { width: 10 policy: sublistview.contentHeight > parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded @@ -174,7 +174,7 @@ MainPopupBase { Rectangle { id: bgrect anchors.fill: parent - color: "#f5f5f5" + color: Style.titleBackgroundColor visible: mouseOver && parent.ListView.view.currentIndex !== delegateItem.index property bool mouseOver: false } @@ -182,7 +182,7 @@ MainPopupBase { id: borderrect implicitHeight: 1 implicitWidth: parent.width - color: "#dcdcdc" + color: Style.popupBorderColor y: parent.height } @@ -237,13 +237,13 @@ MainPopupBase { font.family: Style.fontFamily text: delegateItem.description wrapMode: Text.WordWrap - color: "#1a1a1a" + color: Style.textDescriptionColor } Text { Layout.fillWidth: true elide: Text.ElideRight - color: "#646464" + color: Style.textMetadataColor font.weight: Font.Light visible: typeof(delegateItem.release_date) == "string" && delegateItem.release_date text: qsTr("Released: %1").arg(delegateItem.release_date) @@ -251,7 +251,7 @@ MainPopupBase { Text { Layout.fillWidth: true elide: Text.ElideRight - color: "#646464" + color: Style.textMetadataColor font.weight: Font.Light visible: typeof(delegateItem.url) == "string" && delegateItem.url != "" && delegateItem.url != "internal://format" text: !delegateItem.url ? "" : diff --git a/src/OptionsGeneralTab.qml b/src/OptionsGeneralTab.qml index d45df66e..ac91f509 100644 --- a/src/OptionsGeneralTab.qml +++ b/src/OptionsGeneralTab.qml @@ -59,7 +59,7 @@ OptionsTabBase { } Text { text : ".local" - color: chkHostname.checked ? "black" : "grey" + color: chkHostname.checked ? Style.formLabelColor : Style.formLabelColor } } @@ -79,7 +79,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("Username:") - color: parent.enabled ? (fieldUserName.indicateError ? "red" : "black") : "grey" + color: parent.enabled ? (fieldUserName.indicateError ? Style.formLabelErrorColor : Style.formLabelColor) : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item @@ -104,7 +104,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("Password:") - color: parent.enabled ? (fieldUserPassword.indicateError ? "red" : "black") : "grey" + color: parent.enabled ? (fieldUserPassword.indicateError ? Style.formLabelErrorColor : Style.formLabelColor) : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item @@ -159,7 +159,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("SSID:") - color: chkWifi.checked ? (fieldWifiSSID.indicateError ? "red" : "black") : "grey" + color: chkWifi.checked ? (fieldWifiSSID.indicateError ? Style.formLabelErrorColor : Style.formLabelColor) : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item @@ -181,7 +181,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("Password:") - color: chkWifi.checked ? (fieldWifiPassword.indicateError ? "red" : "black") : "grey" + color: chkWifi.checked ? (fieldWifiPassword.indicateError ? Style.formLabelErrorColor : Style.formLabelColor) : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item @@ -232,7 +232,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("Wireless LAN country:") - color: chkWifi.checked ? "black" : "grey" + color: chkWifi.checked ? Style.formLabelColor : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item @@ -254,7 +254,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("Time zone:") - color: chkLocale.checked ? "black" : "grey" + color: chkLocale.checked ? Style.formLabelColor : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item @@ -273,7 +273,7 @@ OptionsTabBase { RowLayout { Text { text: qsTr("Keyboard layout:") - color: chkLocale.checked ? "black" : "grey" + color: chkLocale.checked ? Style.formLabelColor : Style.formLabelColor Layout.leftMargin: 40 } // Spacer item diff --git a/src/OptionsServicesTab.qml b/src/OptionsServicesTab.qml index 92f63db0..3bda8f45 100644 --- a/src/OptionsServicesTab.qml +++ b/src/OptionsServicesTab.qml @@ -72,7 +72,7 @@ OptionsTabBase { Text { text: qsTr("Set authorized_keys for '%1':").arg(root.fieldUserName.text) - color: radioPubKeyAuthentication.checked ? "black" : "grey" + color: radioPubKeyAuthentication.checked ? Style.formLabelColor : Style.formLabelDisabledColor // textFormat: Text.PlainText Layout.leftMargin: 40 } diff --git a/src/Style.qml b/src/Style.qml index b4d2d2e0..b19d4948 100644 --- a/src/Style.qml +++ b/src/Style.qml @@ -10,6 +10,46 @@ import QtQuick 2.15 Item { id: root + readonly property color mainBackgroundColor: "#cd2355" + + readonly property color buttonBackgroundColor: "#ffffff" + readonly property color buttonForegroundColor: mainBackgroundColor + readonly property color buttonFocusedBackgroundColor: "#d1dcfb" + + readonly property color button2BackgroundColor: mainBackgroundColor + readonly property color button2ForegroundColor: "#ffffff" + readonly property color button2FocusedBackgroundColor: "#32a0d7" + + readonly property color titleBackgroundColor: "#f5f5f5" + readonly property color titleSeparatorColor: "#afafaf" + readonly property color popupBorderColor: "#dcdcdc" + + readonly property color listViewRowBackgroundColor: "#ffffff" + readonly property color listViewHoverRowBackgroundColor: titleBackgroundColor + readonly property color listViewHighlightColor: "lightsteelblue" + + // descriptions in list views + readonly property color textDescriptionColor: "#1a1a1a" + + // OS metadata + readonly property color textMetadataColor: "#646464" + + // for the "device / OS / storage" titles + readonly property color subtitleColor: "#ffffff" + + readonly property color progressBarTextColor: "white" + readonly property color progressBarVerifyForegroundColor: "#6cc04a" + readonly property color progressBarBackgroundColor: "#d15d7d" + + readonly property color lanbarBackgroundColor: "#ffffe3" + + /// the check-boxes/radio-buttons have labels that might be disabled + readonly property color formLabelColor: "black" + readonly property color formLabelErrorColor: "red" + readonly property color formLabelDisabledColor: "grey" + + readonly property color embeddedModeInfoTextColor: "#ffffff" + readonly property alias fontFamily: roboto.name readonly property alias fontFamilyLight: robotoLight.name readonly property alias fontFamilyBold: robotoBold.name diff --git a/src/main.qml b/src/main.qml index 6c45a381..88f4d0c2 100644 --- a/src/main.qml +++ b/src/main.qml @@ -90,7 +90,7 @@ ApplicationWindow { } Rectangle { - color: "#cd2355" + color: Style.mainBackgroundColor implicitWidth: window.width implicitHeight: window.height * (1 - 1/4) @@ -116,7 +116,7 @@ ApplicationWindow { Text { id: text0 - color: "#ffffff" + color: Style.subtitleColor text: qsTr("Raspberry Pi Device") Layout.fillWidth: true Layout.preferredHeight: 17 @@ -154,7 +154,7 @@ ApplicationWindow { Text { id: text1 - color: "#ffffff" + color: Style.subtitleColor text: qsTr("Operating System") Layout.fillWidth: true Layout.preferredHeight: 17 @@ -191,7 +191,7 @@ ApplicationWindow { Text { id: text2 - color: "#ffffff" + color: Style.subtitleColor text: qsTr("Storage") Layout.fillWidth: true Layout.preferredHeight: 17 @@ -231,7 +231,7 @@ ApplicationWindow { Text { id: progressText font.pointSize: 10 - color: "white" + color: Style.progressBarTextColor font.family: Style.fontFamilyBold font.bold: true visible: false @@ -245,7 +245,7 @@ ApplicationWindow { id: progressBar Layout.fillWidth: true visible: false - Material.background: "#d15d7d" + Material.background: Style.progressBarBackgroundColor } } @@ -313,7 +313,7 @@ ApplicationWindow { Text { Layout.columnSpan: 3 - color: "#ffffff" + color: Style.embeddedModeInfoTextColor font.pixelSize: 18 font.family: Style.fontFamily visible: window.imageWriter.isEmbeddedMode() && window.imageWriter.customRepo() @@ -323,7 +323,7 @@ ApplicationWindow { Text { id: networkInfo Layout.columnSpan: 3 - color: "#ffffff" + color: Style.embeddedModeInfoTextColor font.pixelSize: 18 font.family: Style.fontFamily visible: window.imageWriter.isEmbeddedMode() @@ -332,7 +332,7 @@ ApplicationWindow { Text { Layout.columnSpan: 3 - color: "#ffffff" + color: Style.embeddedModeInfoTextColor font.pixelSize: 18 font.family: Style.fontFamily visible: !window.imageWriter.hasMouse() @@ -347,7 +347,7 @@ ApplicationWindow { visible: window.imageWriter.isEmbeddedMode() implicitWidth: langbar.width implicitHeight: langbar.height - color: "#ffffe3" + color: Style.lanbarBackgroundColor radius: 5 RowLayout { @@ -596,7 +596,7 @@ ApplicationWindow { return progressText.text = qsTr("Verifying... %1%").arg(Math.floor(newPos*100)) - progressBar.Material.accent = "#6cc04a" + progressBar.Material.accent = Style.progressBarVerifyForegroundColor progressBar.value = newPos } } diff --git a/src/qmlcomponents/ImButton.qml b/src/qmlcomponents/ImButton.qml index dd66fdab..d2ceb5e3 100644 --- a/src/qmlcomponents/ImButton.qml +++ b/src/qmlcomponents/ImButton.qml @@ -12,8 +12,8 @@ import RpiImager Button { font.family: Style.fontFamily font.capitalization: Font.AllUppercase - Material.background: activeFocus ? "#d1dcfb" : "#ffffff" - Material.foreground: "#cd2355" + Material.background: activeFocus ? Style.buttonFocusedBackgroundColor : Style.buttonBackgroundColor + Material.foreground: Style.buttonForegroundColor Material.roundedScale: Material.ExtraSmallScale Accessible.onPressAction: clicked() Keys.onEnterPressed: clicked() diff --git a/src/qmlcomponents/ImButtonRed.qml b/src/qmlcomponents/ImButtonRed.qml index 35d376f0..dfb94895 100644 --- a/src/qmlcomponents/ImButtonRed.qml +++ b/src/qmlcomponents/ImButtonRed.qml @@ -5,7 +5,9 @@ import QtQuick.Controls.Material 2.2 +import RpiImager + ImButton { - Material.background: activeFocus ? "#32a0d7" : "#cd2355" - Material.foreground: "#ffffff" + Material.background: activeFocus ? Style.button2FocusedBackgroundColor : Style.mainBackgroundColor + Material.foreground: Style.button2ForegroundColor } diff --git a/src/qmlcomponents/ImPopup.qml b/src/qmlcomponents/ImPopup.qml index f9a4078c..56b37610 100644 --- a/src/qmlcomponents/ImPopup.qml +++ b/src/qmlcomponents/ImPopup.qml @@ -29,7 +29,7 @@ Popup { // background of title Rectangle { id: msgpopup_title_background - color: "#f5f5f5" + color: Style.titleBackgroundColor anchors.left: parent.left anchors.top: parent.top height: 35 @@ -53,7 +53,7 @@ Popup { // line under title Rectangle { id: msgpopup_title_separator - color: "#afafaf" + color: Style.titleSeparatorColor width: parent.width anchors.top: msgpopup_title_background.bottom height: 1 From bdbd8f3fa694493d6cd6981f708cae383d7bbab1 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Mon, 17 Feb 2025 19:50:02 +0000 Subject: [PATCH 33/47] Move the hardware list model to C++ Allows us to use declarative bindings and less imperative JavaScript: - hwlist.currentIndex now has a proper binding to C++ and is not assigned in random GUI places - OSPopup doesn't need to access HWPopup.qml internals anymore, as the hw list is auto sufficient now. - HWListModel now directly passes the filtered tags to ImageWritter, as this is business logic, the gui code doesn't need to be involved. --- src/.zed/settings.json | 7 ++ src/CMakeLists.txt | 5 +- src/HwPopup.qml | 44 ++++--------- src/OSPopup.qml | 18 +----- src/hwlistmodel.cpp | 143 +++++++++++++++++++++++++++++++++++++++++ src/hwlistmodel.h | 69 ++++++++++++++++++++ src/imagewriter.cpp | 25 +++++-- src/imagewriter.h | 17 ++++- src/main.qml | 3 +- 9 files changed, 272 insertions(+), 59 deletions(-) create mode 100644 src/.zed/settings.json create mode 100644 src/hwlistmodel.cpp create mode 100644 src/hwlistmodel.h diff --git a/src/.zed/settings.json b/src/.zed/settings.json new file mode 100644 index 00000000..4259ffee --- /dev/null +++ b/src/.zed/settings.json @@ -0,0 +1,7 @@ +// Folder-specific settings +// +// For a full list of overridable settings, and general information on folder-specific settings, +// see the documentation: https://zed.dev/docs/configuring-zed#settings-files +{ + "format_on_save": "off" +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90f3af59..27bace14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -171,7 +171,9 @@ set(CURL_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/dependencies/curl-8.11.0/include # Adding headers explicity so they are displayed in Qt Creator set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelistitem.h drivelistmodel.h drivelistmodelpollthread.h driveformatthread.h powersaveblocker.h cli.h devicewrapper.h devicewrapperblockcacheentry.h devicewrapperpartition.h devicewrapperstructs.h devicewrapperfatpartition.h wlancredentials.h - downloadthread.h downloadextractthread.h localfileextractthread.h downloadstatstelemetry.h dependencies/mountutils/src/mountutils.hpp dependencies/sha256crypt/sha256crypt.h) + downloadthread.h downloadextractthread.h localfileextractthread.h downloadstatstelemetry.h dependencies/mountutils/src/mountutils.hpp dependencies/sha256crypt/sha256crypt.h + hwlistmodel.h +) # Add dependencies if (APPLE) @@ -343,6 +345,7 @@ set_source_files_properties(Style.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) # C++ types exposed to QML set(IMAGER_QML_CPP_TYPES imagewriter.cpp + hwlistmodel.cpp ) qt_add_qml_module(${PROJECT_NAME} diff --git a/src/HwPopup.qml b/src/HwPopup.qml index ae1b8aea..a12dd11c 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -17,11 +17,12 @@ MainPopupBase { id: root property string hwselected: "" - property alias deviceModel: deviceModel property alias hwlist: hwlist required property ListView oslist required property SwipeView osswipeview required property ListModel osmodel + required property ImageWriter imageWriter + readonly property HWListModel deviceModel: imageWriter.getHWList() title: qsTr("Raspberry Pi Device") @@ -29,27 +30,18 @@ MainPopupBase { id: hwlist anchors.right: parent.right - model: ListModel { - id: deviceModel - ListElement { - name: qsTr("[ All ]") - tags: "[]" - icon: "" - description: "" - matching_type: "exclusive" - } - } - currentIndex: -1 + model: root.deviceModel + currentIndex: root.deviceModel.currentIndex delegate: hwdelegate anchors.top: root.title_separator.bottom Keys.onSpacePressed: { if (currentIndex != -1) - root.selectHWitem(model.get(currentIndex)) + root.selectHWitem(currentIndex) } Accessible.onPressAction: { if (currentIndex != -1) - root.selectHWitem(model.get(currentIndex)) + root.selectHWitem(currentIndex) } } @@ -86,7 +78,7 @@ MainPopupBase { } onClicked: { - root.selectHWitem(delegateRoot.modelData) + root.selectHWitem(delegateRoot.index) } } @@ -154,26 +146,13 @@ MainPopupBase { } } - function selectHWitem(hwmodel) { - /* Default is exclusive matching */ - var inclusive = false - - if (hwmodel.matching_type) { - switch (hwmodel.matching_type) { - case "exclusive": - break; - case "inclusive": - inclusive = true - break; - } - } - - imageWriter.setHWFilterList(hwmodel.tags, inclusive) + function selectHWitem(index) { + root.deviceModel.setSelectedIndex(index); /* Reload list */ - var oslist_json = imageWriter.getFilteredOSlist(); + var oslist_json = root.imageWriter.getFilteredOSlist(); var o = JSON.parse(oslist_json) - var oslist_parsed = oslistFromJson(o) + var oslist_parsed = oslistFromJson(o) // qmllint disable unqualified if (oslist_parsed === false) return @@ -209,7 +188,6 @@ MainPopupBase { osbutton.text = qsTr("CHOOSE OS") writebutton.enabled = false - hwbutton.text = hwmodel.name root.close() } } diff --git a/src/OSPopup.qml b/src/OSPopup.qml index ca29aab1..0d30efed 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -22,6 +22,7 @@ MainPopupBase { property alias osmodel: osmodel required property ImageWriter imageWriter + readonly property HWListModel hwmodel: root.imageWriter.getHWList() title: qsTr("Operating System") @@ -404,24 +405,11 @@ MainPopupBase { osmodel.append(oslist_parsed[i]) } + root.hwmodel.reload() + if ("imager" in o) { var imager = o["imager"] - if ("devices" in imager) - { - hwpopup.deviceModel.clear() - var devices = imager["devices"] - for (var j in devices) - { - devices[j]["tags"] = JSON.stringify(devices[j]["tags"]) - hwpopup.deviceModel.append(devices[j]) - if ("default" in devices[j] && devices[j]["default"]) - { - hwpopup.hwlist.currentIndex = hwpopup.deviceModel.count-1 - } - } - } - if (root.imageWriter.getBoolSetting("check_version") && "latest_version" in imager && "url" in imager) { if (!root.imageWriter.isEmbeddedMode() && root.imageWriter.isVersionNewer(imager["latest_version"])) { updatepopup.url = imager["url"] diff --git a/src/hwlistmodel.cpp b/src/hwlistmodel.cpp new file mode 100644 index 00000000..614eb00d --- /dev/null +++ b/src/hwlistmodel.cpp @@ -0,0 +1,143 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +#include "hwlistmodel.h" +#include "imagewriter.h" + +#include +#include +#include +#include +#include + +HWListModel::HWListModel(ImageWriter &imageWriter) + : QAbstractListModel(&imageWriter), _imageWriter(imageWriter) {} + +bool HWListModel::reload() +{ + QJsonDocument doc = _imageWriter.getFilteredOSlistDocument(); + QJsonObject root = doc.object(); + + if (root.isEmpty() || !doc.isObject()) { + qWarning() << Q_FUNC_INFO << "Invalid root"; + return false; + } + + QJsonValue imager = root.value("imager"); + + if (!imager.isObject()) { + qWarning() << Q_FUNC_INFO << "missing imager"; + return false; + } + + QJsonValue devices = imager.toObject().value("devices"); + + if (!devices.isArray()) { + // just means list hasn't been loaded yet + return false; + } + + beginResetModel(); + _currentIndex = -1; + + const QJsonArray deviceArray = devices.toArray(); + _hwDevices.reserve(deviceArray.size()); + int indexOfDefault = -1; + for (const QJsonValue &deviceValue: deviceArray) { + QJsonObject deviceObj = deviceValue.toObject(); + + HardwareDevice hwDevice = { + deviceObj["name"].toString(), + deviceObj["tags"].toArray(), + deviceObj["icon"].toString(), + deviceObj["description"].toString(), + deviceObj["matching_type"].toString() + }; + _hwDevices.append(hwDevice); + + if (deviceObj["default"].isBool() && deviceObj["default"].toBool()) + indexOfDefault = _hwDevices.size() - 1; + } + + endResetModel(); + + setCurrentIndex(indexOfDefault); + + return true; +} + +void HWListModel::setSelectedIndex(int index) +{ + if (index < 0 || index >= _hwDevices.size()) { + qWarning() << Q_FUNC_INFO << "Invalid index" << index; + return; + } + + const HardwareDevice &device = _hwDevices.at(index); + + _imageWriter.setHWFilterList(device.tags, device.isInclusive()); + + setCurrentIndex(index); +} + +int HWListModel::rowCount(const QModelIndex &) const +{ + return _hwDevices.size(); +} + +QHash HWListModel::roleNames() const +{ + return { + {NameRole, "name"}, + {TagsRole, "tags"}, + {IconRole, "icon"}, + {DescriptionRole, "description"}, + {MatchingTypeRole, "matching_type"} + }; +} + +QVariant HWListModel::data(const QModelIndex &index, int role) const { + const int row = index.row(); + if (row < 0 || row >= _hwDevices.size()) + return {}; + + const HardwareDevice &device = _hwDevices[row]; + + switch (HWListRole(role)) { + case NameRole: + return device.name; + case TagsRole: + return device.tags; + case IconRole: + return device.icon; + case DescriptionRole: + return device.description; + case MatchingTypeRole: + return device.matchingType; + } + + return {}; +} + +QString HWListModel::currentName() const { + if (_currentIndex < 0 || _currentIndex >= _hwDevices.size()) + return tr("CHOOSE DEVICE"); + + HardwareDevice device = _hwDevices[_currentIndex]; + return device.name; +} + +void HWListModel::setCurrentIndex(int index) { + if (_currentIndex == index) + return; + _currentIndex = index; + + Q_EMIT currentIndexChanged(); + Q_EMIT currentNameChanged(); +} + +int HWListModel::currentIndex() const { + return _currentIndex; +} diff --git a/src/hwlistmodel.h b/src/hwlistmodel.h new file mode 100644 index 00000000..aa217d9d --- /dev/null +++ b/src/hwlistmodel.h @@ -0,0 +1,69 @@ +#ifndef HWLISTMODEL_H +#define HWLISTMODEL_H + +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +#include + +#include +#include + +class ImageWriter; + +class HWListModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("Created by C++") + Q_PROPERTY(QString currentName READ currentName NOTIFY currentNameChanged) + Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged) +public: + + enum HWListRole { + NameRole = Qt::UserRole + 1, + TagsRole, + IconRole, + DescriptionRole, + MatchingTypeRole + }; + + struct HardwareDevice { + QString name; + QJsonArray tags; + QString icon; + QString description; + QString matchingType; + + bool isInclusive() const { + return matchingType == QLatin1String("inclusive"); + } + }; + + explicit HWListModel(ImageWriter &); + + Q_INVOKABLE void setSelectedIndex(int index); + Q_INVOKABLE bool reload(); + + QString currentName() const; + int currentIndex() const; +Q_SIGNALS: + void currentNameChanged(); + void currentIndexChanged(); + +protected: + int rowCount(const QModelIndex &) const override; + QHash roleNames() const override; + QVariant data(const QModelIndex &index, int role) const override; + +private: + void setCurrentIndex(int index); + + QVector _hwDevices; + ImageWriter &_imageWriter; + int _currentIndex = -1; +}; + +#endif diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 2b60144f..fac961d9 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +64,8 @@ ImageWriter::ImageWriter(QObject *parent) : QObject(parent), _repo(QUrl(QString(OSLIST_URL))), _dlnow(0), _verifynow(0), _engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false), _embeddedMode(false), _online(false), _customCacheFile(false), _trans(nullptr), - _networkManager(this) + _networkManager(this), + _hwlist(HWListModel(*this)) // explicitly parented, so QML doesn't delete it { connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress())); @@ -512,9 +514,8 @@ namespace { } // namespace anonymous -void ImageWriter::setHWFilterList(const QByteArray &json, const bool &inclusive) { - QJsonDocument json_document = QJsonDocument::fromJson(json); - _deviceFilter = json_document.array(); +void ImageWriter::setHWFilterList(const QJsonArray &tags, const bool &inclusive) { + _deviceFilter = tags; _deviceFilterIsInclusive = inclusive; } @@ -619,7 +620,12 @@ namespace { } } // namespace anonymous -QByteArray ImageWriter::getFilteredOSlist() { +QByteArray ImageWriter::getFilteredOSlist() +{ + return getFilteredOSlistDocument().toJson(); +} + +QJsonDocument ImageWriter::getFilteredOSlistDocument() { QJsonArray reference_os_list_array = {}; QJsonObject reference_imager_metadata = {}; { @@ -655,14 +661,14 @@ QByteArray ImageWriter::getFilteredOSlist() { {"imager", reference_imager_metadata}, {"os_list", reference_os_list_array}, } - )).toJson(); + )); } void ImageWriter::beginOSListFetch() { QNetworkRequest request = QNetworkRequest(constantOsListUrl()); request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); - + // This will set up a chain of requests that culiminate in the eventual fetch and assembly of // a complete cached OS list. _networkManager.get(request); @@ -693,6 +699,11 @@ DriveListModel *ImageWriter::getDriveList() return &_drivelist; } +HWListModel *ImageWriter::getHWList() +{ + return &_hwlist; +} + void ImageWriter::startProgressPolling() { _powersave.applyBlock(tr("Downloading and writing image")); diff --git a/src/imagewriter.h b/src/imagewriter.h index 3036556b..da59a71b 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -18,9 +18,12 @@ #include #include #include +#include +#include #include "config.h" #include "powersaveblocker.h" #include "drivelistmodel.h" +#include "hwlistmodel.h" class QQmlApplicationEngine; class DownloadThread; @@ -64,9 +67,15 @@ class ImageWriter : public QObject /* Stop polling the list of available drives */ Q_INVOKABLE void stopDriveListPolling(); - /* Return list of available drives. Call startDriveListPolling() first */ + /* Return list of available drives. Call startDriveListPolling() first + Note: If you mark this as Q_INVOKABLE, be sure to parent it to ImageWriter, + to prevent GC from deleting it. + */ DriveListModel *getDriveList(); + /* Return list of available devices. */ + Q_INVOKABLE HWListModel *getHWList(); + /* Utility function to return filename part from URL */ Q_INVOKABLE QString fileNameFromUrl(const QUrl &url); @@ -85,11 +94,14 @@ class ImageWriter : public QObject /* Get the cached OS list. This may be empty if network connectivity is not available. */ Q_INVOKABLE QByteArray getFilteredOSlist(); + /* Overload which returns QJsonDocument */ + Q_INVOKABLE QJsonDocument getFilteredOSlistDocument(); + /** Begin the asynchronous fetch of the OS lists, and associated sublists. */ Q_INVOKABLE void beginOSListFetch(); /** Set the HW filter, for a filtered view of the OS list */ - Q_INVOKABLE void setHWFilterList(const QByteArray &json, const bool &inclusive); + Q_INVOKABLE void setHWFilterList(const QJsonArray &tags, const bool &inclusive); /* Set custom cache file */ void setCustomCacheFile(const QString &cacheFile, const QByteArray &sha256); @@ -197,6 +209,7 @@ protected slots: QByteArray _expectedHash, _cachedFileHash, _cmdline, _config, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat; quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow; DriveListModel _drivelist; + HWListModel _hwlist; QQmlApplicationEngine *_engine; QTimer _polltimer, _networkchecktimer; PowerSaveBlocker _powersave; diff --git a/src/main.qml b/src/main.qml index 88f4d0c2..caa133f4 100644 --- a/src/main.qml +++ b/src/main.qml @@ -129,7 +129,7 @@ ApplicationWindow { ImButton { id: hwbutton - text: qsTr("CHOOSE DEVICE") + text: window.imageWriter.getHWList().currentName spacing: 0 padding: 0 bottomPadding: 0 @@ -436,6 +436,7 @@ ApplicationWindow { osmodel: ospopup.osmodel osswipeview: ospopup.osswipeview windowWidth: window.width + imageWriter: window.imageWriter } OSPopup { From ff73b474b7dcdbeb8a677a306393cebfb794810e Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 19 Feb 2025 15:23:04 +0000 Subject: [PATCH 34/47] Remove unused hwselected property --- src/HwPopup.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/HwPopup.qml b/src/HwPopup.qml index a12dd11c..db01f660 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -16,7 +16,6 @@ import RpiImager MainPopupBase { id: root - property string hwselected: "" property alias hwlist: hwlist required property ListView oslist required property SwipeView osswipeview From 0f7a03c46a5981ec4b3bc966d22b9695960b612a Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 19 Feb 2025 15:43:28 +0000 Subject: [PATCH 35/47] Fix TODO about property type 'int' not being enough In JavaScript it's "double" --- src/OSPopup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OSPopup.qml b/src/OSPopup.qml index 0d30efed..a0484fe6 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -143,7 +143,7 @@ MainPopupBase { required property string subitems_json required property string extract_sha256 required property QtObject model - required property int image_download_size // KDAB + required property double image_download_size property string website property string tooltip From d5f7b6bbcacae2bc7894af733fc6fbbfba5b615f Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 19 Feb 2025 16:51:08 +0000 Subject: [PATCH 36/47] Move the OSPopup.qml model to C++ This allows to remove loads of JavaScript, which is not a type-safe language. Also allows to remove interdependencies/workarounds regarding sharing popup internals with each other. Now the C++ model is the source of truth. While moving from QML to C++, it was important to preserve the model structure, to avoid too much rewrite in a single commit and not create regressions. The model remains flat, as that's the less intrusive way to adapt the GUI. We can now consider a true tree-view model, but needs to be evaluated for GUI rework impact. --- src/CMakeLists.txt | 2 + src/HwPopup.qml | 34 ++----- src/OSPopup.qml | 35 +++---- src/hwlistmodel.cpp | 26 +++--- src/hwlistmodel.h | 16 ++-- src/imagewriter.cpp | 8 +- src/imagewriter.h | 5 + src/main.qml | 62 +------------ src/oslistmodel.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++++ src/oslistmodel.h | 82 +++++++++++++++++ 10 files changed, 356 insertions(+), 131 deletions(-) create mode 100644 src/oslistmodel.cpp create mode 100644 src/oslistmodel.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 27bace14..0b7820b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -173,6 +173,7 @@ set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelist devicewrapper.h devicewrapperblockcacheentry.h devicewrapperpartition.h devicewrapperstructs.h devicewrapperfatpartition.h wlancredentials.h downloadthread.h downloadextractthread.h localfileextractthread.h downloadstatstelemetry.h dependencies/mountutils/src/mountutils.hpp dependencies/sha256crypt/sha256crypt.h hwlistmodel.h + oslistmodel.h ) # Add dependencies @@ -346,6 +347,7 @@ set_source_files_properties(Style.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE) set(IMAGER_QML_CPP_TYPES imagewriter.cpp hwlistmodel.cpp + oslistmodel.cpp ) qt_add_qml_module(${PROJECT_NAME} diff --git a/src/HwPopup.qml b/src/HwPopup.qml index db01f660..1c8f18c8 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -17,10 +17,13 @@ MainPopupBase { id: root property alias hwlist: hwlist + + // These two could go away if their 'currentIndex' used bindings instead of direct assignment required property ListView oslist required property SwipeView osswipeview - required property ListModel osmodel + required property ImageWriter imageWriter + readonly property OSListModel osModel: imageWriter.getOSList() readonly property HWListModel deviceModel: imageWriter.getHWList() title: qsTr("Raspberry Pi Device") @@ -146,36 +149,18 @@ MainPopupBase { } function selectHWitem(index) { - root.deviceModel.setSelectedIndex(index); + // this calls the C++ setter, which sets the image writer device filter + root.deviceModel.currentIndex = index - /* Reload list */ - var oslist_json = root.imageWriter.getFilteredOSlist(); - var o = JSON.parse(oslist_json) - var oslist_parsed = oslistFromJson(o) // qmllint disable unqualified - if (oslist_parsed === false) - return + /* Reload OS list since filters changed */ + root.osModel.reload() /* As we're filtering the OS list, we need to ensure we present a 'Recommended' OS. * To do this, we exploit a convention of how we build the OS list. By convention, * the preferred OS for a device is listed at the top level of the list, and is at the * lowest index. So.. */ - if (oslist_parsed.length != 0) { - var candidate = oslist_parsed[0] - - if ("description" in candidate && - !("subitems" in candidate) && - !candidate["description"].includes("(Recommended)") - ) - { - candidate["description"] += " (Recommended)" - } - } - - root.osmodel.clear() - for (var i in oslist_parsed) { - root.osmodel.append(oslist_parsed[i]) - } + root.osModel.markFirstAsRecommended() // When the HW device is changed, reset the OS selection otherwise // you get a weird effect with the selection moving around in the list @@ -183,7 +168,6 @@ MainPopupBase { // an OS selected which isn't compatible with this HW device oslist.currentIndex = -1 osswipeview.currentIndex = 0 - imageWriter.setSrc("") osbutton.text = qsTr("CHOOSE OS") writebutton.enabled = false diff --git a/src/OSPopup.qml b/src/OSPopup.qml index a0484fe6..078f9a98 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -19,10 +19,11 @@ MainPopupBase { property string categorySelected : "" property alias oslist: oslist property alias osswipeview: osswipeview - property alias osmodel: osmodel + required property MsgPopup updatepopup required property ImageWriter imageWriter readonly property HWListModel hwmodel: root.imageWriter.getHWList() + readonly property OSListModel osmodel: root.imageWriter.getOSList() title: qsTr("Operating System") @@ -41,7 +42,7 @@ MainPopupBase { MainPopupListViewBase { id: oslist - model: osmodel + model: root.osmodel currentIndex: -1 delegate: osdelegate anchors.top: parent.top @@ -64,13 +65,9 @@ MainPopupBase { } } - ListModel { - id: osmodel - - Component.onCompleted: { - if (root.imageWriter.isOnline()) { - root.fetchOSlist(); - } + Component.onCompleted: { + if (root.imageWriter.isOnline()) { + root.fetchOSlist(); } } @@ -130,6 +127,7 @@ MainPopupBase { } Component { + // delegate is used by both main view and sub-views id: osdelegate Item { @@ -377,7 +375,7 @@ MainPopupBase { return m } - /// Is the item a sub-list or sub-sub-list in the OS selection model? + /// Returns whether this item has children function isOSsublist(d) { // Top level category if (typeof(d.subitems_json) == "string" && d.subitems_json !== "") { @@ -395,25 +393,18 @@ MainPopupBase { } function fetchOSlist() { - var oslist_json = root.imageWriter.getFilteredOSlist(); - var o = JSON.parse(oslist_json) - var oslist_parsed = oslistFromJson(o) // qmllint disable unqualified - if (oslist_parsed === false) - return - osmodel.clear() - for (var i in oslist_parsed) { - osmodel.append(oslist_parsed[i]) - } - + /// Reload OS model and HW model as well due to filtering + root.osmodel.reload() root.hwmodel.reload() + var o = JSON.parse(root.imageWriter.getFilteredOSlist()) if ("imager" in o) { var imager = o["imager"] if (root.imageWriter.getBoolSetting("check_version") && "latest_version" in imager && "url" in imager) { if (!root.imageWriter.isEmbeddedMode() && root.imageWriter.isVersionNewer(imager["latest_version"])) { - updatepopup.url = imager["url"] - updatepopup.openPopup() + root.updatepopup.url = imager["url"] + root.updatepopup.openPopup() } } if ("default_os" in imager) { diff --git a/src/hwlistmodel.cpp b/src/hwlistmodel.cpp index 614eb00d..c7d088b5 100644 --- a/src/hwlistmodel.cpp +++ b/src/hwlistmodel.cpp @@ -10,7 +10,6 @@ #include #include #include -#include HWListModel::HWListModel(ImageWriter &imageWriter) : QAbstractListModel(&imageWriter), _imageWriter(imageWriter) {} @@ -68,20 +67,6 @@ bool HWListModel::reload() return true; } -void HWListModel::setSelectedIndex(int index) -{ - if (index < 0 || index >= _hwDevices.size()) { - qWarning() << Q_FUNC_INFO << "Invalid index" << index; - return; - } - - const HardwareDevice &device = _hwDevices.at(index); - - _imageWriter.setHWFilterList(device.tags, device.isInclusive()); - - setCurrentIndex(index); -} - int HWListModel::rowCount(const QModelIndex &) const { return _hwDevices.size(); @@ -132,6 +117,17 @@ QString HWListModel::currentName() const { void HWListModel::setCurrentIndex(int index) { if (_currentIndex == index) return; + + if (index < 0 || index >= _hwDevices.size()) { + qWarning() << Q_FUNC_INFO << "Invalid index" << index; + return; + } + + const HardwareDevice &device = _hwDevices.at(index); + + _imageWriter.setHWFilterList(device.tags, device.isInclusive()); + _imageWriter.setSrc({}); + _currentIndex = index; Q_EMIT currentIndexChanged(); diff --git a/src/hwlistmodel.h b/src/hwlistmodel.h index aa217d9d..69d39c5f 100644 --- a/src/hwlistmodel.h +++ b/src/hwlistmodel.h @@ -1,13 +1,12 @@ -#ifndef HWLISTMODEL_H -#define HWLISTMODEL_H - /* * SPDX-License-Identifier: Apache-2.0 * Copyright (C) 2020 Raspberry Pi Ltd */ -#include +#ifndef HWLISTMODEL_H +#define HWLISTMODEL_H +#include #include #include @@ -19,7 +18,7 @@ class HWListModel : public QAbstractListModel QML_ELEMENT QML_UNCREATABLE("Created by C++") Q_PROPERTY(QString currentName READ currentName NOTIFY currentNameChanged) - Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) public: enum HWListRole { @@ -44,11 +43,14 @@ class HWListModel : public QAbstractListModel explicit HWListModel(ImageWriter &); - Q_INVOKABLE void setSelectedIndex(int index); Q_INVOKABLE bool reload(); + // Returns the name associated with the current index QString currentName() const; + int currentIndex() const; + void setCurrentIndex(int index); + Q_SIGNALS: void currentNameChanged(); void currentIndexChanged(); @@ -59,8 +61,6 @@ class HWListModel : public QAbstractListModel QVariant data(const QModelIndex &index, int role) const override; private: - void setCurrentIndex(int index); - QVector _hwDevices; ImageWriter &_imageWriter; int _currentIndex = -1; diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index fac961d9..ff4e22bc 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -65,7 +65,8 @@ ImageWriter::ImageWriter(QObject *parent) _engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false), _embeddedMode(false), _online(false), _customCacheFile(false), _trans(nullptr), _networkManager(this), - _hwlist(HWListModel(*this)) // explicitly parented, so QML doesn't delete it + _hwlist(HWListModel(*this)), // explicitly parented, so QML doesn't delete it + _oslist(OSListModel(*this)) { connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress())); @@ -704,6 +705,11 @@ HWListModel *ImageWriter::getHWList() return &_hwlist; } +OSListModel *ImageWriter::getOSList() +{ + return &_oslist; +} + void ImageWriter::startProgressPolling() { _powersave.applyBlock(tr("Downloading and writing image")); diff --git a/src/imagewriter.h b/src/imagewriter.h index da59a71b..6835dce0 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -24,6 +24,7 @@ #include "powersaveblocker.h" #include "drivelistmodel.h" #include "hwlistmodel.h" +#include "oslistmodel.h" class QQmlApplicationEngine; class DownloadThread; @@ -76,6 +77,9 @@ class ImageWriter : public QObject /* Return list of available devices. */ Q_INVOKABLE HWListModel *getHWList(); + /* Return list of available devices. */ + Q_INVOKABLE OSListModel *getOSList(); + /* Utility function to return filename part from URL */ Q_INVOKABLE QString fileNameFromUrl(const QUrl &url); @@ -210,6 +214,7 @@ protected slots: quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow; DriveListModel _drivelist; HWListModel _hwlist; + OSListModel _oslist; QQmlApplicationEngine *_engine; QTimer _polltimer, _networkchecktimer; PowerSaveBlocker _powersave; diff --git a/src/main.qml b/src/main.qml index caa133f4..e2555f53 100644 --- a/src/main.qml +++ b/src/main.qml @@ -171,6 +171,7 @@ ApplicationWindow { padding: 0 bottomPadding: 0 topPadding: 0 + // text: window.imageWriter.getOSList().currentName TODO Layout.minimumHeight: 40 Layout.fillWidth: true onClicked: { @@ -433,7 +434,6 @@ ApplicationWindow { HwPopup { id: hwpopup oslist: ospopup.oslist - osmodel: ospopup.osmodel osswipeview: ospopup.osswipeview windowWidth: window.width imageWriter: window.imageWriter @@ -443,6 +443,7 @@ ApplicationWindow { id: ospopup windowWidth: window.width imageWriter: window.imageWriter + updatepopup: updatepopup } DstPopup { @@ -673,29 +674,6 @@ ApplicationWindow { networkInfo.text = msg } - function shuffle(arr) { - for (var i = 0; i < arr.length - 1; i++) { - var j = i + Math.floor(Math.random() * (arr.length - i)); - - var t = arr[j]; - arr[j] = arr[i]; - arr[i] = t; - } - } - - function checkForRandom(list) { - for (var i in list) { - var entry = list[i] - - if ("subitems" in entry) { - checkForRandom(entry["subitems"]) - if ("random" in entry && entry["random"]) { - shuffle(entry["subitems"]) - } - } - } - } - function filterItems(list, tags, matchingType) { if (!tags || !tags.length) @@ -778,42 +756,6 @@ ApplicationWindow { } } - function oslistFromJson(o) { - var oslist_parsed = false - var lang_country = Qt.locale().name - if ("os_list_"+lang_country in o) { - oslist_parsed = o["os_list_"+lang_country] - } - else if (lang_country.includes("_")) { - var lang = lang_country.substr(0, lang_country.indexOf("_")) - if ("os_list_"+lang in o) { - oslist_parsed = o["os_list_"+lang] - } - } - - if (!oslist_parsed) { - if (!"os_list" in o) { - onError(qsTr("Error parsing os_list.json")) - return false - } - - oslist_parsed = o["os_list"] - } - - checkForRandom(oslist_parsed) - - /* Flatten subitems to subitems_json */ - for (var i in oslist_parsed) { - var entry = oslist_parsed[i]; - if ("subitems" in entry) { - entry["subitems_json"] = JSON.stringify(entry["subitems"]) - delete entry["subitems"] - } - } - - return oslist_parsed - } - Timer { /* Verify if default drive is in our list after 100 ms */ id: setDefaultDest diff --git a/src/oslistmodel.cpp b/src/oslistmodel.cpp new file mode 100644 index 00000000..790998c5 --- /dev/null +++ b/src/oslistmodel.cpp @@ -0,0 +1,217 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +#include "oslistmodel.h" +#include "imagewriter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + + QJsonArray getListForLocale(QJsonObject root) { + // "os_list_" has priority + + QString localeName = QLocale::system().name(); + QString candidateKey = QStringLiteral("os_list_%1").arg(localeName); + + QJsonArray list; + if (root.contains(candidateKey)) { + list = root[candidateKey].toArray(); + } else if (localeName.contains(QLatin1Char('_'))) { + localeName = localeName.section('_', 0, 0); + candidateKey = QStringLiteral("os_list_%1").arg(localeName); + if (root.contains(candidateKey)) { + list = root[candidateKey].toArray(); + } + } + + // fallback to "os_list" + if (list.isEmpty() && root.contains(QLatin1String("os_list"))) { + list = root["os_list"].toArray(); + } + + return list; + } + + QJsonArray parseOSJson(QJsonObject root) { + QJsonArray list = getListForLocale(root); + if (list.isEmpty()) { + qWarning() << Q_FUNC_INFO << "Expected to find os_list key" << root.keys(); + return {}; + } + + // Apply random shuffling to arrays containing 'random' flag + std::function shuffleIfRandom = [&](QJsonArray &lst) { + for (int i = 0; i < lst.size(); i++) { + QJsonObject entry = lst[i].toObject(); + if (entry.contains(QLatin1String("subitems"))) { + QJsonArray subitems = entry["subitems"].toArray(); + shuffleIfRandom(subitems); + if (entry.contains(QLatin1String("random")) && entry["random"].toBool()) { + for (int j = 0; j < subitems.size() - 1; j++) { + int k = j + (QRandomGenerator::global()->bounded(subitems.size() - j)); + auto temp = subitems[k]; + subitems[k] = subitems[j]; + subitems[j] = temp; + } + } + entry["subitems"] = subitems; + lst[i] = entry; + } + } + }; + + shuffleIfRandom(list); + + // Flatten, since GUI doesn't support a tree model + for (int i = 0; i < list.size(); i++) { + QJsonObject entry = list[i].toObject(); + if (entry.contains("subitems")) { + QJsonDocument subitemsDoc(entry["subitems"].toArray()); + entry["subitems_json"] = QString::fromUtf8(subitemsDoc.toJson()); + entry.remove("subitems"); + list[i] = entry; + } + } + + return list; + } +} + +OSListModel::OSListModel(ImageWriter &imageWriter) + : QAbstractListModel(&imageWriter), _imageWriter(imageWriter) {} + +bool OSListModel::reload() +{ + QJsonDocument doc = _imageWriter.getFilteredOSlistDocument(); + QJsonObject root = doc.object(); + + QJsonArray list = parseOSJson(root); + if (list.isEmpty()) + return false; + + beginResetModel(); + _osList.clear(); + _osList.reserve(list.count()); + + for (const auto value : list) { + const QJsonObject obj = value.toObject(); + OS os; + + os.name = obj["name"].toString(); + os.description = obj["description"].toString(); + + QJsonArray devicesArray = obj["devices"].toArray(); + os.devices.reserve(devicesArray.size()); + for (const auto &device : devicesArray) { + os.devices.append(device.toString()); + } + + os.extractSize = obj["extract_size"].toDouble(); + os.imageDownloadSize = obj["image_download_size"].toDouble(); + + os.random = obj["random"].toBool(); + + os.extractSha256 = obj["extract_sha256"].toString(); + os.icon = obj["icon"].toString(); + os.initFormat = obj["init_format"].toString(); + os.releaseDate = obj["release_date"].toString(); + os.url = obj["url"].toString(); + os.subitemsJson = obj["subitems_json"].toString(); + os.tooltip = obj["tooltip"].toString(); + os.website = obj["website"].toString(); + + _osList.append(os); + } + + endResetModel(); + + return true; +} + +int OSListModel::rowCount(const QModelIndex &) const +{ + return _osList.size(); +} + +QHash OSListModel::roleNames() const +{ + return { + { NameRole, "name" }, + { DescriptionRole, "description" }, + { DevicesRole, "devices" }, + { ExtractSha256Role, "extract_sha256" }, + { ExtractSizeRole, "extract_size" }, + { IconRole, "icon" }, + { ImageDownloadSizeRole, "image_download_size" }, + { InitFormatRole, "init_format" }, + { ReleaseDataRole, "release_date" }, + { UrlRole, "url" },{ RandomRole, "random" }, + { SubItemsJsonRole, "subitems_json" }, + { TooltipRole, "tooltip" }, + { WebsiteRole, "website" } + }; +} + +QVariant OSListModel::data(const QModelIndex &index, int role) const { + const int row = index.row(); + if (row < 0 || row >= _osList.size()) + return {}; + + const OS &os = _osList[row]; + + switch (OSListRole(role)) { + case NameRole: + return os.name; + case DescriptionRole: + return os.description; + case DevicesRole: + return os.devices; + case ExtractSha256Role: + return os.extractSha256; + case ExtractSizeRole: + return os.extractSize; + case IconRole: + return os.icon; + case ImageDownloadSizeRole: + return os.imageDownloadSize; + case InitFormatRole: + return os.initFormat; + case ReleaseDataRole: + return os.releaseDate; + case UrlRole: + return os.url; + case RandomRole: + return os.random; + case SubItemsJsonRole: + return os.subitemsJson; + case TooltipRole: + return os.tooltip; + case WebsiteRole: + return os.website; + } + + return {}; +} + +void OSListModel::markFirstAsRecommended() { + if (!_osList.isEmpty()) { + OS &candidate = _osList[0]; + + if (!candidate.description.isEmpty() && + candidate.subitemsJson.isEmpty() && + !candidate.description.contains(QLatin1String("(Recommended)"))) + { + candidate.description += QLatin1String(" (Recommended)"); + } + } +} diff --git a/src/oslistmodel.h b/src/oslistmodel.h new file mode 100644 index 00000000..8ba70e62 --- /dev/null +++ b/src/oslistmodel.h @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +#ifndef OSLISTMODEL_H +#define OSLISTMODEL_H + +#include +#include + +class ImageWriter; + +/* + Implements a model for OSPopup.qml + + The model is flat since it moved from QML to C++, the current GUI is expecting a list model + not a tree model. The next step for improvement would be turning this into a tree model instead of storing + JSON in 'subitemsJson', + a QSortFilterProxyModel so we can change root index easily. +*/ +class OSListModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("Created by C++") +public: + + enum OSListRole { + NameRole = Qt::UserRole + 1, + DescriptionRole, + DevicesRole, + ExtractSha256Role, + ExtractSizeRole, + IconRole, + ImageDownloadSizeRole, + InitFormatRole, + ReleaseDataRole, + UrlRole, + RandomRole, + SubItemsJsonRole, + TooltipRole, + WebsiteRole, + }; + + struct OS { + QString name; + QString description; + QStringList devices; // not used by QML but present in JSON + QString icon; + QString initFormat; + QString releaseDate; + QString url; + QString subitemsJson; + QString subitemsUrl; + QString tooltip; + QString website; + QString extractSha256; + + quint64 imageDownloadSize = 0; + quint64 extractSize = 0; + + bool random = false; + }; + + explicit OSListModel(ImageWriter &); + + Q_INVOKABLE bool reload(); + + // Adds "(Recommended)" to the description of the first OS + Q_INVOKABLE void markFirstAsRecommended(); + +protected: + int rowCount(const QModelIndex &) const override; + QHash roleNames() const override; + QVariant data(const QModelIndex &index, int role) const override; + +private: + QVector _osList; + ImageWriter &_imageWriter; +}; + +#endif From debff9fef26fb8376e11ab81e721befd7a604a71 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 19 Feb 2025 17:31:42 +0000 Subject: [PATCH 37/47] Don't allow HwPopup/OSPopup to edit properties from main.qml Notify main.qml via a signal. No need to pass external buttons and list views into the popups anymore. This makes them more self-contained. --- src/HwPopup.qml | 16 +++------------- src/OSPopup.qml | 6 +++--- src/main.qml | 19 ++++++++++++++++--- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/HwPopup.qml b/src/HwPopup.qml index 1c8f18c8..33e1cfee 100644 --- a/src/HwPopup.qml +++ b/src/HwPopup.qml @@ -18,14 +18,12 @@ MainPopupBase { property alias hwlist: hwlist - // These two could go away if their 'currentIndex' used bindings instead of direct assignment - required property ListView oslist - required property SwipeView osswipeview - required property ImageWriter imageWriter readonly property OSListModel osModel: imageWriter.getOSList() readonly property HWListModel deviceModel: imageWriter.getHWList() + signal deviceSelected + title: qsTr("Raspberry Pi Device") MainPopupListViewBase { @@ -162,15 +160,7 @@ MainPopupBase { */ root.osModel.markFirstAsRecommended() - // When the HW device is changed, reset the OS selection otherwise - // you get a weird effect with the selection moving around in the list - // when the user next opens the OS list, and the user could still have - // an OS selected which isn't compatible with this HW device - oslist.currentIndex = -1 - osswipeview.currentIndex = 0 - osbutton.text = qsTr("CHOOSE OS") - writebutton.enabled = false - + root.deviceSelected() root.close() } } diff --git a/src/OSPopup.qml b/src/OSPopup.qml index 078f9a98..d99620f5 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -20,11 +20,12 @@ MainPopupBase { property alias oslist: oslist property alias osswipeview: osswipeview - required property MsgPopup updatepopup required property ImageWriter imageWriter readonly property HWListModel hwmodel: root.imageWriter.getHWList() readonly property OSListModel osmodel: root.imageWriter.getOSList() + signal updatePopupRequested(var url) + title: qsTr("Operating System") onClosed: { @@ -403,8 +404,7 @@ MainPopupBase { if (root.imageWriter.getBoolSetting("check_version") && "latest_version" in imager && "url" in imager) { if (!root.imageWriter.isEmbeddedMode() && root.imageWriter.isVersionNewer(imager["latest_version"])) { - root.updatepopup.url = imager["url"] - root.updatepopup.openPopup() + root.updatePopupRequested(imager["url"]) } } if ("default_os" in imager) { diff --git a/src/main.qml b/src/main.qml index e2555f53..d2ef76d5 100644 --- a/src/main.qml +++ b/src/main.qml @@ -433,17 +433,30 @@ ApplicationWindow { HwPopup { id: hwpopup - oslist: ospopup.oslist - osswipeview: ospopup.osswipeview windowWidth: window.width imageWriter: window.imageWriter + + onDeviceSelected: { + // When the HW device is changed, reset the OS selection otherwise + // you get a weird effect with the selection moving around in the list + // when the user next opens the OS list, and the user could still have + // an OS selected which isn't compatible with this HW device + ospopup.oslist.currentIndex = -1 + ospopup.osswipeview.currentIndex = 0 + osbutton.text = qsTr("CHOOSE OS") + writebutton.enabled = false + } } OSPopup { id: ospopup windowWidth: window.width imageWriter: window.imageWriter - updatepopup: updatepopup + + onUpdatePopupRequested: (url) => { + updatepopup.url = url + updatepopup.openPopup() + } } DstPopup { From dbea8c2ec8bb05e8c3970de85fb8fd7ec5bd874b Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Wed, 19 Feb 2025 18:40:52 +0000 Subject: [PATCH 38/47] Remove unneeded filterItems JS function --- src/main.qml | 82 ---------------------------------------------------- 1 file changed, 82 deletions(-) diff --git a/src/main.qml b/src/main.qml index d2ef76d5..16e84104 100644 --- a/src/main.qml +++ b/src/main.qml @@ -687,88 +687,6 @@ ApplicationWindow { networkInfo.text = msg } - function filterItems(list, tags, matchingType) - { - if (!tags || !tags.length) - return - - var i = list.length - while (i--) { - var entry = list[i] - - if ("devices" in entry && entry["devices"].length) { - var foundTag = false - - switch(matchingType) { - case 0: /* exact matching */ - case 2: /* exact matching */ - for (var j in tags) - { - if (entry["devices"].includes(tags[j])) - { - foundTag = true - break - } - } - /* If there's no match, remove this item from the list. */ - if (!foundTag) - { - list.splice(i, 1) - continue - } - break - case 1: /* Exlusive by prefix matching */ - case 3: /* Inclusive by prefix matching */ - for (var deviceTypePrefix in tags) { - for (var deviceSpec in entry["devices"]) { - if (deviceSpec.startsWith(deviceTypePrefix)) { - foundTag = true - break - } - } - /* Terminate outer loop early if we've already - * decided it's a match - */ - if (foundTag) { - break - } - } - /* If there's no match, remove this item from the list. */ - if (!foundTag) - { - list.splice(i, 1) - continue - } - break - } - } else { - /* No device list attached? If we're in an exclusive mode that's bad news indeed. */ - switch (matchingType) { - case 0: - case 1: - if (!("subitems" in entry)) { - /* If you're not carrying subitems, you're not going in. */ - list.splice(i, 1) - } - break - case 2: - case 3: - /* Inclusive filtering. We're keeping this one. */ - break; - } - } - - if ("subitems" in entry) { - filterItems(entry["subitems"], tags, hwTagMatchingType) - - // If this sub-list has no items then hide it - if (entry["subitems"].length == 0) { - list.splice(i, 1) - } - } - } - } - Timer { /* Verify if default drive is in our list after 100 ms */ id: setDefaultDest From b5b62c06722075ff2e1363198a7b145aef38c8ed Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 20 Feb 2025 11:15:27 +0000 Subject: [PATCH 39/47] Make ImageWritter::getDriveList() invocable from QML This makes it consistent with the other 2 C++ models. Since it's now invocable, it needs a QObject parent to avoid deletion by garbage collector. --- src/DstPopup.qml | 3 +-- src/imagewriter.cpp | 3 ++- src/imagewriter.h | 2 +- src/main.cpp | 4 +--- src/main.qml | 3 +-- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/DstPopup.qml b/src/DstPopup.qml index dcff59e9..d9a6305f 100644 --- a/src/DstPopup.qml +++ b/src/DstPopup.qml @@ -17,7 +17,6 @@ MainPopupBase { id: root required property ImageWriter imageWriter - required property DriveListModel driveListModel property alias dstlist: dstlist onClosed: imageWriter.stopDriveListPolling() @@ -26,7 +25,7 @@ MainPopupBase { MainPopupListViewBase { id: dstlist - model: root.driveListModel + model: root.imageWriter.getDriveList() delegate: dstdelegate anchors.top: root.title_separator.bottom anchors.right: parent.right diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index ff4e22bc..44c6aac7 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -65,7 +65,8 @@ ImageWriter::ImageWriter(QObject *parent) _engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false), _embeddedMode(false), _online(false), _customCacheFile(false), _trans(nullptr), _networkManager(this), - _hwlist(HWListModel(*this)), // explicitly parented, so QML doesn't delete it + _drivelist(DriveListModel(this)), // explicitly parented, so QML doesn't delete it + _hwlist(HWListModel(*this)), _oslist(OSListModel(*this)) { connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress())); diff --git a/src/imagewriter.h b/src/imagewriter.h index 6835dce0..663a19da 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -72,7 +72,7 @@ class ImageWriter : public QObject Note: If you mark this as Q_INVOKABLE, be sure to parent it to ImageWriter, to prevent GC from deleting it. */ - DriveListModel *getDriveList(); + Q_INVOKABLE DriveListModel *getDriveList(); /* Return list of available devices. */ Q_INVOKABLE HWListModel *getHWList(); diff --git a/src/main.cpp b/src/main.cpp index a7df72c7..cbef2bf5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,7 +11,6 @@ #include #include #include "imagewriter.h" -#include "drivelistmodel.h" #include "networkaccessmanagerfactory.h" #include "cli.h" #include @@ -342,8 +341,7 @@ int main(int argc, char *argv[]) imageWriter.setEngine(&engine); engine.setNetworkAccessManagerFactory(&namf); - engine.setInitialProperties(QVariantMap{{"imageWriter", QVariant::fromValue(&imageWriter)}, - {"driveListModel", QVariant::fromValue(imageWriter.getDriveList())}}); + engine.setInitialProperties(QVariantMap{{"imageWriter", QVariant::fromValue(&imageWriter)}}); engine.load(QUrl(QStringLiteral("qrc:/RpiImager/main.qml"))); if (engine.rootObjects().isEmpty()) diff --git a/src/main.qml b/src/main.qml index 16e84104..f27fd296 100644 --- a/src/main.qml +++ b/src/main.qml @@ -17,7 +17,7 @@ ApplicationWindow { visible: true required property ImageWriter imageWriter - required property DriveListModel driveListModel + readonly property DriveListModel driveListModel: imageWriter.getDriveList() width: imageWriter.isEmbeddedMode() ? -1 : 680 height: imageWriter.isEmbeddedMode() ? -1 : 450 @@ -462,7 +462,6 @@ ApplicationWindow { DstPopup { id: dstpopup imageWriter: window.imageWriter - driveListModel: window.driveListModel windowWidth: window.width } From 96cab7079aa945050d1a4d6d8c8dd61f4e6362bc Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Thu, 20 Feb 2025 11:36:30 +0000 Subject: [PATCH 40/47] Don't access internal main.qml timers from OSPopup.qml Fixes a qmllint warning about unqualified access. Further increases decoupling. The embedded block could be moved to C++ but I don't have a way to test the embedded mode, so don't want to risk regressions there. --- src/OSPopup.qml | 4 ++-- src/main.qml | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/OSPopup.qml b/src/OSPopup.qml index d99620f5..633a5aa8 100644 --- a/src/OSPopup.qml +++ b/src/OSPopup.qml @@ -25,6 +25,7 @@ MainPopupBase { readonly property OSListModel osmodel: root.imageWriter.getOSList() signal updatePopupRequested(var url) + signal defaultEmbeddedDriveRequested(var drive) title: qsTr("Operating System") @@ -416,8 +417,7 @@ MainPopupBase { } if ("embedded_default_destination" in imager) { root.imageWriter.startDriveListPolling() - setDefaultDest.drive = imager["embedded_default_destination"] - setDefaultDest.start() + root.defaultEmbeddedDriveRequested(imager["embedded_default_destination"]) } } } diff --git a/src/main.qml b/src/main.qml index f27fd296..239561ec 100644 --- a/src/main.qml +++ b/src/main.qml @@ -457,6 +457,11 @@ ApplicationWindow { updatepopup.url = url updatepopup.openPopup() } + + onDefaultEmbeddedDriveRequested: (drive) => { + setDefaultDest.drive = drive + setDefaultDest.start() + } } DstPopup { From f12096fe32e0c70230bd81a52f48a668c2689c9e Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 21 Feb 2025 14:36:49 +0000 Subject: [PATCH 41/47] Translate the "(Recommended)" OS string Since we don't have control of what actually is in the JSON list, check for the translation variant as well, to be safe. --- src/oslistmodel.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/oslistmodel.cpp b/src/oslistmodel.cpp index 790998c5..bcb1c907 100644 --- a/src/oslistmodel.cpp +++ b/src/oslistmodel.cpp @@ -207,11 +207,15 @@ void OSListModel::markFirstAsRecommended() { if (!_osList.isEmpty()) { OS &candidate = _osList[0]; + const QString recommendedString = QStringLiteral(" (Recommended)"); + const QString recommendedStringLocalized = QStringLiteral(" (%1)").arg(tr("Recommended")); + if (!candidate.description.isEmpty() && candidate.subitemsJson.isEmpty() && - !candidate.description.contains(QLatin1String("(Recommended)"))) + !candidate.description.contains(recommendedString) && + !candidate.description.contains(recommendedStringLocalized)) { - candidate.description += QLatin1String(" (Recommended)"); + candidate.description += recommendedStringLocalized; } } } From 3b72fadd6a8964ef85911bc0c7cc1bb33b832ba5 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 21 Feb 2025 15:30:23 +0000 Subject: [PATCH 42/47] cmake: Add an rpi-imager_lupdate target Whenever moving strings around you should call this target, so .ts files are updated, so translators can act on them. Also fixes some tr()s not working after they moved from QML to C++ --- src/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0b7820b7..6416d688 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -287,6 +287,9 @@ qt_add_translation(QM_FILES ${TRANSLATIONS}) configure_file(i18n/translations.qrc "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc ${QM_FILES}) +# Create an rpi-imager_lupdate target +qt_add_lupdate(TS_FILES ${TRANSLATIONS} SOURCE_TARGETS ${PROJECT_NAME}) + if (WIN32) # Adding WIN32 prevents a console window being opened on Windows add_executable(${PROJECT_NAME} WIN32 ${SOURCES} ${HEADERS} ${DEPENDENCIES}) From 64a2b55cc4f29fc88d8cc0f9f93d0692e99e513f Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Fri, 21 Feb 2025 15:33:30 +0000 Subject: [PATCH 43/47] Refresh .ts files Fixes some tr()s not working after they moved from QML to C++. The command was: ninja rpi-imager_lupdate --- src/i18n/rpi-imager_ca.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_de.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_en.ts | 487 +++++++++++++++++------------- src/i18n/rpi-imager_es.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_fr.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_he.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_it.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_ja.ts | 549 +++++++++++++++++++++++----------- src/i18n/rpi-imager_ko.ts | 547 +++++++++++++++++++++++----------- src/i18n/rpi-imager_nl.ts | 547 +++++++++++++++++++++++----------- src/i18n/rpi-imager_pl.ts | 547 +++++++++++++++++++++++----------- src/i18n/rpi-imager_pt.ts | 547 +++++++++++++++++++++++----------- src/i18n/rpi-imager_ru.ts | 549 +++++++++++++++++++++++----------- src/i18n/rpi-imager_sk.ts | 545 +++++++++++++++++++++++----------- src/i18n/rpi-imager_sl.ts | 561 ++++++++++++++++++++++------------- src/i18n/rpi-imager_tr.ts | 468 +++++++++++++++++------------ src/i18n/rpi-imager_uk.ts | 547 +++++++++++++++++++++++----------- src/i18n/rpi-imager_zh-TW.ts | 547 +++++++++++++++++++++++----------- src/i18n/rpi-imager_zh.ts | 545 +++++++++++++++++++++++----------- 19 files changed, 6831 insertions(+), 3425 deletions(-) diff --git a/src/i18n/rpi-imager_ca.ts b/src/i18n/rpi-imager_ca.ts index cb870e2d..ce21edb4 100644 --- a/src/i18n/rpi-imager_ca.ts +++ b/src/i18n/rpi-imager_ca.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 S'ha produït un error en extreure l'arxiu: %1 - + Error mounting FAT32 partition S'ha produït un error en muntar la partició FAT32 - + Operating system did not mount FAT32 partition El sistema operatiu no ha muntat la partició FAT32 - + Error changing to directory '%1' S'ha produït un error en canviar al directori «%1» @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive S'està desmuntant el dispositiu - + opening drive S'està obrint la unitat - + Error running diskpart: %1 S'ha produït un error en executar «diskpart»: %1 - + Error removing existing partitions S'ha produït un error en eliminar les particions existents. - + Authentication cancelled S'ha cancel·lat l'autenticació - + Error running authopen to gain access to disk device '%1' S'ha produït un error en executar «authopen» per a obtenir l'accés al dispositiu de disc «%1» - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Verifiqueu si el «Raspberry Pi Imager» té accés als «volums extraïbles» des de la configuració de privacitat (sota «fitxers i carpetes» o doneu-li «accés complet al disc») - + Cannot open storage device '%1'. No s'ha pogut obrir el dispositiu d'emmagatzematge «%1» - + discarding existing data on drive S'estan descartant les dades existents a la unitat - + zeroing out first and last MB of drive S'està esborrant amb zeros el primer i l'últim MB de la unitat - + Write error while zero'ing out MBR S'ha produït un error en esborrar amb zeros l'«MBR». - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). S'ha produït un error d'escriptura en esborrar amb zeros l'última part de la targeta.<br>La targeta podria estar indicant una capacitat errònia (possible falsificació) - + starting download S'està iniciant la baixada - + Error downloading: %1 S'ha produït un error en la baixada: %1 - + Access denied error while writing file to disk. S'ha produït un error d'accés denegat en escriure el fitxer al disc. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. L'opció «Controla l'accés de la carpeta» de la Seguretat del Windows sembla que està activada. Afegiu els executables «rpi-imager.exe» i «fat32format.exe» a la llista d'aplicacions permeses i torneu-ho a provar. - + Error writing file to disk S'ha produït un error en escriure el fitxer al disc - + Download corrupt. Hash does not match La baixada està corrompuda. El «hash» no coincideix - - + + Error writing to storage (while flushing) S'ha produït un error en escriure a l'emmagatzematge (procés: Flushing) - - + + Error writing to storage (while fsync) S'ha produït un error en escriure a l'emmagatzematge (procés: fsync) - + Error writing first block (partition table) S'ha produït un error en escriure el primer bloc (taula de particions) - + Error reading from storage.<br>SD card may be broken. S'ha produït un error en llegir l'emmagatzematge.<br>És possible que la targeta SD estigui malmesa. - + Verifying write failed. Contents of SD card is different from what was written to it. Ha fallat la verificació de l'escriptura. El contingut de la targeta SD és diferent del que s'hi ha escrit. - + Customizing image S'està personalitzant la imatge @@ -210,40 +210,105 @@ Aquesta plataforma no té implementada la formatació + + DstPopup + + + Storage + Emmagatzematge + + + + No storage devices found + No s'ha trobat cap dispositiu d'emmagatzematge + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Muntat com a %1 + + + + GB + + + + + [WRITE PROTECTED] + [PROTEGIT CONTRA ESCRIPTURA] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + La targeta SD està protegida contra escriptura.<br>Accioneu l'interruptor del costat esquerre de la targeta SD per tal que quedi posicionat a la part superior i torneu-ho a provar. + + + + HWListModel + + + CHOOSE DEVICE + TRIA LA PLACA + + + + HwPopup + + + Raspberry Pi Device + Placa Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. La capacitat de l'emmagatzematge no és suficient.<br>Ha de ser de %1 GB com a mínim. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. El fitxer d'entrada no és una imatge de disc vàlida.<br>La mida del fitxer és de %1 bytes, que no és múltiple de 512 bytes. - + Downloading and writing image S'està baixant i escrivint la imatge - + Select image Selecciona una imatge - + Error synchronizing time. Trying again in 3 seconds S'ha produït un error en sincronitzar l'hora. Torneu-ho a provar en 3 segons. - + STP is enabled on your Ethernet switch. Getting IP will take long time. El commutador d'Ethernet té l'STP activat. L'obtenció de la IP pot trigar molt. - + Would you like to prefill the wifi password from the system keychain? Voleu emplenar la contrasenya del wifi des del clauer del sistema? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file S'està obrint el fitxer de la imatge - + Error opening image file S'ha produït un error en obrir el fitxer de la imatge @@ -264,159 +329,309 @@ MsgPopup - + NO NO - + YES - + CONTINUE CONTINUA - + QUIT SURT + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Sistema operatiu + + + + Back + Enrere + + + + Go back to main menu + Torna al menú principal + + + + Released: %1 + Llançat el: %1 + + + + Cached on your computer + A la memòria cau de l'ordinador + + + + Local file + Fitxer local + + + + Online - %1 GB download + Disponible en línia (%1 GB) + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Connecteu una memòria USB que contingui primer imatges.<br>Les imatges s'han de trobar a la carpeta arrel de la memòria. + + + + OptionsGeneralTab + + + Set hostname: + Defineix un nom de la màquina (hostname): + + + + Set username and password + Defineix el nom d'usuari i contrasenya + + + + Username: + Nom d'usuari: + + + + + Password: + Contrasenya: + + + + Configure wireless LAN + Configura la wifi + + + + SSID: + SSID: + + + + Show password + Mostra la contrasenya + + + + Hidden SSID + SSID oculta + + + + Wireless LAN country: + País del wifi: + + + + Set locale settings + Estableix la configuració regional + + + + Time zone: + Fus horari: + + + + Keyboard layout: + Disposició del teclat: + + + + OptionsMiscTab + + + Play sound when finished + Fes un so quan acabi + + + + Eject media when finished + Expulsa el mitjà quan acabi + + + + Enable telemetry + Activa la telemetria + + OptionsPopup - + OS Customization Personalització del SO - + General General - + Services Serveis - + Options Opcions - Set hostname: - Defineix un nom de la màquina (hostname): + Defineix un nom de la màquina (hostname): - Set username and password - Defineix el nom d'usuari i contrasenya + Defineix el nom d'usuari i contrasenya - Username: - Nom d'usuari: + Nom d'usuari: - - Password: - Contrasenya: + Contrasenya: - Configure wireless LAN - Configura la wifi + Configura la wifi - SSID: - SSID: + SSID: - Show password - Mostra la contrasenya + Mostra la contrasenya - Hidden SSID - SSID oculta + SSID oculta - Wireless LAN country: - País del wifi: + País del wifi: - Set locale settings - Estableix la configuració regional + Estableix la configuració regional - Time zone: - Fus horari: + Fus horari: - Keyboard layout: - Disposició del teclat: + Disposició del teclat: - Enable SSH - Activa el protocol SSH + Activa el protocol SSH - Use password authentication - Utilitza l'autenticació de contrasenya + Utilitza l'autenticació de contrasenya - Allow public-key authentication only - Permet només l'autenticació de claus públiques + Permet només l'autenticació de claus públiques - Set authorized_keys for '%1': - Establiu «authorized_keys» per a l'usuari «%1»: + Establiu «authorized_keys» per a l'usuari «%1»: - RUN SSH-KEYGEN - EXECUTA SSH-KEYGEN + EXECUTA SSH-KEYGEN - Play sound when finished - Fes un so quan acabi + Fes un so quan acabi - Eject media when finished - Expulsa el mitjà quan acabi + Expulsa el mitjà quan acabi - Enable telemetry - Activa la telemetria + Activa la telemetria - + SAVE DESA + + OptionsServicesTab + + + Enable SSH + Activa el protocol SSH + + + + Use password authentication + Utilitza l'autenticació de contrasenya + + + + Allow public-key authentication only + Permet només l'autenticació de claus públiques + + + + Set authorized_keys for '%1': + Establiu «authorized_keys» per a l'usuari «%1»: + + + + Delete Key + + + + + RUN SSH-KEYGEN + EXECUTA SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Lector de targetes SD intern @@ -424,32 +639,31 @@ UseSavedSettingsPopup - Use OS customization? - Voleu personalitzar el SO? + Voleu personalitzar el SO? - + Would you like to apply OS customization settings? Voleu aplicar la configuració personalitzada del SO? - + NO NO - + NO, CLEAR SETTINGS NO, ESBORRA LA CONFIGURACIÓ - + YES - + EDIT SETTINGS EDITA LA CONFIGURACIÓ @@ -457,275 +671,256 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Placa Raspberry Pi - CHOOSE DEVICE - TRIA LA PLACA + TRIA LA PLACA - + Select this button to choose your target Raspberry Pi Seleccioneu aquest botó per a escollir la placa Raspberry Pi objectiu - - + Operating System Sistema operatiu - - + + CHOOSE OS TRIA EL SO - + Select this button to change the operating system Seleccioneu aquest botó si voleu canviar el sistema operatiu - - + Storage Emmagatzematge - + Network not ready yet La xarxa encara no és a punt - No storage devices found - No s'ha trobat cap dispositiu d'emmagatzematge + No s'ha trobat cap dispositiu d'emmagatzematge - - + + CHOOSE STORAGE TRIA L'EMMAGATZEMATGE - + Select this button to change the destination storage device Seleccioneu aquest botó per a canviar la destinació del dispositiu d'emmagatzematge - + CANCEL WRITE CANCEL·LA L'ESCRIPTURA - - + + Cancelling... S'està cancel·lant... - + CANCEL VERIFY CANCEL·LA LA VERIFICACIÓ - - - + + + Finalizing... S'està finalitzant... - + Next Següent - + Select this button to start writing the image Seleccioneu aquest botó per a començar l'escriptura de la imatge - + Using custom repository: %1 S'està usant el repositori personalitzat: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Navegació per teclat: <tab> navega al botó següent <espai> prem el botó o selecciona l'element <fletxa amunt o avall> desplaçament per les llistes - + Language: Idioma: - + Keyboard: Teclat: - [ All ] - [ TOT ] + [ TOT ] - Back - Enrere + Enrere - Go back to main menu - Torna al menú principal + Torna al menú principal - Released: %1 - Llançat el: %1 + Llançat el: %1 - Cached on your computer - A la memòria cau de l'ordinador + A la memòria cau de l'ordinador - Local file - Fitxer local + Fitxer local - Online - %1 GB download - Disponible en línia (%1 GB) + Disponible en línia (%1 GB) - - - Mounted as %1 - Muntat com a %1 + Muntat com a %1 - [WRITE PROTECTED] - [PROTEGIT CONTRA ESCRIPTURA] + [PROTEGIT CONTRA ESCRIPTURA] - + Are you sure you want to quit? Esteu segur que en voleu sortir? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? El Raspberry Pi Imager està ocupat.<br>Esteu segur que en voleu sortir? - + Warning Avís - + Preparing to write... S'està preparant per a escriure... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Totes les dades existents a «%1» s'esborraran.<br>Esteu segur que voleu continuar? - + Update available Hi ha una actualització disponible - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Hi ha una nova versió de l'Imager disponible.<br>Voleu visitar el lloc web per baixar-la? - + Writing... %1% S'està escrivint... %1% - + Verifying... %1% S'està verificant... %1% - + Preparing to write... (%1) S'està preparant per escriure... (%1) - + Error S'ha produït un error - + Write Successful S'ha escrit amb èxit - - + + Erase Esborra - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader S'ha esborrat <b>%1</b><br><br>Ja podeu retirar la targeta SD del lector - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader S'ha escrit el «<b>%1</b>» a <b>%2</b><br><br>Ja podeu retirar la targeta SD del lector - Error parsing os_list.json - S'ha produït un error en analitzar os_lists.json + S'ha produït un error en analitzar os_lists.json - + Format card as FAT32 Formata la targeta com a FAT32 - + Use custom Utilitza una personalitzada - + Select a custom .img from your computer Selecciona una imatge .img personalitzada de l'ordinador - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Connecteu una memòria USB que contingui primer imatges.<br>Les imatges s'han de trobar a la carpeta arrel de la memòria. + Connecteu una memòria USB que contingui primer imatges.<br>Les imatges s'han de trobar a la carpeta arrel de la memòria. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - La targeta SD està protegida contra escriptura.<br>Accioneu l'interruptor del costat esquerre de la targeta SD per tal que quedi posicionat a la part superior i torneu-ho a provar. + La targeta SD està protegida contra escriptura.<br>Accioneu l'interruptor del costat esquerre de la targeta SD per tal que quedi posicionat a la part superior i torneu-ho a provar. diff --git a/src/i18n/rpi-imager_de.ts b/src/i18n/rpi-imager_de.ts index 56a1856e..453f338d 100644 --- a/src/i18n/rpi-imager_de.ts +++ b/src/i18n/rpi-imager_de.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Fehler beim Entpacken des Archivs: %1 - + Error mounting FAT32 partition Fehler beim Einbinden der FAT32-Partition - + Operating system did not mount FAT32 partition Das Betriebssystem band die FAT32-Partition nicht ein - + Error changing to directory '%1' Fehler beim Wechseln in den Ordner "%1" @@ -32,125 +32,125 @@ DownloadThread - + unmounting drive Laufwerk wird ausgehängt - + opening drive Laufwerk wird geöffnet - + Error running diskpart: %1 Fehler beim Ausführen von Diskpart: %1 - + Error removing existing partitions Fehler beim Entfernen von existierenden Partitionen - + Authentication cancelled Authentifizierung abgebrochen - + Error running authopen to gain access to disk device '%1' Fehler beim Ausführen von authopen, um Zugriff auf Geräte zu erhalten '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Not sure if current macOS has that option (or if it got moved/renamed) Bitte stellen Sie sicher, dass 'Raspberry Pi Imager' Zugriff auf 'removable volumes' in privacy settings hat (unter 'files and folders'. Sie können ihm auch 'full disk access' geben). - + Cannot open storage device '%1'. Speichergerät '%1' kann nicht geöffnet werden. - + discarding existing data on drive Vorhandene Daten auf dem Medium werden gelöscht - + zeroing out first and last MB of drive Erstes und letztes Megabyte des Mediums werden überschrieben - + Write error while zero'ing out MBR Schreibfehler während des Löschens des MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Fehler beim Löschen des letzten Teiles der Speicherkarte.<br>Die Speicherkarte könnte mit einer falschen Größe beworben sein (möglicherweise Betrug). - + starting download Download wird gestartet - + Error downloading: %1 Fehler beim Herunterladen: %1 - + Access denied error while writing file to disk. Zugriff verweigert-Fehler beim Schreiben auf den Datenträger. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. "Überwachter Ordnerzugriff" scheint aktiviert zu sein. Bitte fügen Sie sowohl rpi-imager.exe als auch fat32format.exe zur Liste der erlaubten Apps hinzu und versuchen sie es erneut. - + Error writing file to disk Fehler beim Schreiben der Datei auf den Speicher - + Download corrupt. Hash does not match Download beschädigt. Prüfsumme stimmt nicht überein - - + + Error writing to storage (while flushing) Fehler beim Schreiben auf den Speicher (während flushing) - - + + Error writing to storage (while fsync) Fehler beim Schreiben auf den Speicher (während fsync) - + Error writing first block (partition table) Fehler beim Schreiben des ersten Blocks (Partitionstabelle) - + Error reading from storage.<br>SD card may be broken. Fehler beim Lesen vom Speicher.<br>Die SD-Karte könnte defekt sein. - + Verifying write failed. Contents of SD card is different from what was written to it. Verifizierung fehlgeschlagen. Der Inhalt der SD-Karte weicht von dem Inhalt ab, der geschrieben werden sollte. - + Customizing image Image modifizieren @@ -251,40 +251,105 @@ Formatieren wird auf dieser Platform nicht unterstützt + + DstPopup + + + Storage + SD-Karte + + + + No storage devices found + Keine SD-Karte gefunden + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Als %1 eingebunden + + + + GB + + + + + [WRITE PROTECTED] + [SCHREIBGESCHÜTZT] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + Die Speicherkarte ist schreibgeschützt.<br>Schieben Sie den Schutzschalter auf der linken Seite nach oben, und versuchen Sie es erneut. + + + + HWListModel + + + CHOOSE DEVICE + MODELL WÄHLEN + + + + HwPopup + + + Raspberry Pi Device + Raspberry Pi Modell + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Die Speicherkapazität ist nicht groß genug.<br>Sie muss mindestens %1 GB betragen. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Die Eingabedatei ist kein gültiges Disk-Image.<br>Die Dateigröße%1 Bytes ist kein Vielfaches von 512 Bytes. - + Downloading and writing image Image herunterladen und schreiben - + Select image Image wählen - + Error synchronizing time. Trying again in 3 seconds Fehler beim Synchronisieren der Zeit. Neuer Versuch in 3 Sekunden - + STP is enabled on your Ethernet switch. Getting IP will take long time. STP ist auf Ihrem Ethernet-Switch aktiviert. Das Abrufen der IP wird lange dauern. - + Would you like to prefill the wifi password from the system keychain? Möchten Sie das Wifi-Passwort aus dem System-Schlüsselbund vorab ausfüllen? @@ -292,12 +357,12 @@ LocalFileExtractThread - + opening image file Abbilddatei wird geöffnet - + Error opening image file Fehler beim Öffnen der Imagedatei @@ -305,30 +370,163 @@ MsgPopup - + NO NEIN - + YES JA - + CONTINUE WEITER - + QUIT BEENDEN + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Betriebssystem (OS) + + + + Back + Zurück + + + + Go back to main menu + Zurück zum Hauptmenü + + + + Released: %1 + Veröffentlicht: %1 + + + + Cached on your computer + Auf Ihrem Computer zwischengespeichert + + + + Local file + Lokale Datei + + + + Online - %1 GB download + Online - %1 GB Download + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Verbinden Sie zuerst einen USB-Stick mit Images.<br>Die Images müssen sich im Wurzelverzeichnes des USB-Sticks befinden. + + + + OptionsGeneralTab + + + Set hostname: + Hostname: + + + + Set username and password + Benutzername und Passwort festlegen + + + + Username: + Benutzername: + + + + + Password: + Passwort: + + + + Configure wireless LAN + Wifi einrichten + + + + SSID: + SSID: + + + + Show password + Passwort anzeigen + + + + Hidden SSID + Verborgene SSID + + + + Wireless LAN country: + Wifi-Land: + + + + Set locale settings + Spracheinstellungen festlegen + + + + Time zone: + Zeitzone: + + + + Keyboard layout: + Tastaturlayout: + + + + OptionsMiscTab + + + Play sound when finished + Tonsignal nach Beenden abspielen + + + + Eject media when finished + Medien nach Beenden auswerfen + + + + Enable telemetry + Telemetrie aktivieren + + OptionsPopup - + OS Customization OS Anpassungen @@ -345,123 +543,102 @@ immer verwenden - + General Allgemein - + Services Dienste - + Options Optionen - Set hostname: - Hostname: + Hostname: - Set username and password - Benutzername und Passwort festlegen + Benutzername und Passwort festlegen - Username: - Benutzername: + Benutzername: - - Password: - Passwort: + Passwort: - Configure wireless LAN - Wifi einrichten + Wifi einrichten - SSID: - SSID: + SSID: - Show password - Passwort anzeigen + Passwort anzeigen - Hidden SSID - Verborgene SSID + Verborgene SSID - Wireless LAN country: - Wifi-Land: + Wifi-Land: - Set locale settings - Spracheinstellungen festlegen + Spracheinstellungen festlegen - Time zone: - Zeitzone: + Zeitzone: - Keyboard layout: - Tastaturlayout: + Tastaturlayout: - Enable SSH - SSH aktivieren + SSH aktivieren - Use password authentication - Passwort zur Authentifizierung verwenden + Passwort zur Authentifizierung verwenden - Allow public-key authentication only - Authentifizierung via Public-Key + Authentifizierung via Public-Key - Set authorized_keys for '%1': - authorized_keys für '%1': + authorized_keys für '%1': - RUN SSH-KEYGEN - SSH-KEYGEN ausführen + SSH-KEYGEN ausführen - Play sound when finished - Tonsignal nach Beenden abspielen + Tonsignal nach Beenden abspielen - Eject media when finished - Medien nach Beenden auswerfen + Medien nach Beenden auswerfen - Enable telemetry - Telemetrie aktivieren + Telemetrie aktivieren - + SAVE SPEICHERN @@ -482,10 +659,48 @@ Dauerhafte Einstellungen + + OptionsServicesTab + + + Enable SSH + SSH aktivieren + + + + Use password authentication + Passwort zur Authentifizierung verwenden + + + + Allow public-key authentication only + Authentifizierung via Public-Key + + + + Set authorized_keys for '%1': + authorized_keys für '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + SSH-KEYGEN ausführen + + + + Add SSH Key + + + QObject - + Internal SD card reader Interner SD-Kartenleser @@ -493,32 +708,31 @@ UseSavedSettingsPopup - Use OS customization? - OS Anpassungen anwenden? + OS Anpassungen anwenden? - + Would you like to apply OS customization settings? Möchten Sie die vorher festgelegten OS Anpassungen anwenden? - + NO NEIN - + NO, CLEAR SETTINGS NEIN, EINSTELLUNGEN LÖSCHEN - + YES JA - + EDIT SETTINGS EINSTELLUNGEN BEARBEITEN @@ -526,62 +740,57 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Raspberry Pi Modell - CHOOSE DEVICE - MODELL WÄHLEN + MODELL WÄHLEN - + Select this button to choose your target Raspberry Pi Klicken Sie auf diesen Knopf, um den gewünschten Raspberry Pi auszuwählen - - + Operating System Betriebssystem (OS) - - + + CHOOSE OS OS WÄHLEN - + Select this button to change the operating system Klicken Sie auf diesen Knopf, um das Betriebssystem zu ändern - - + Storage SD-Karte - + Network not ready yet Netzwerk noch nicht bereit - No storage devices found - Keine SD-Karte gefunden + Keine SD-Karte gefunden - - + + CHOOSE STORAGE SD-KARTE WÄHLEN @@ -590,40 +799,40 @@ SCHREIBEN - + Select this button to change the destination storage device Klicken Sie auf diesen Knopf, um das Ziel-Speichermedium zu ändern - + CANCEL WRITE SCHREIBEN ABBRECHEN - - + + Cancelling... Abbrechen... - + CANCEL VERIFY VERIFIZIERUNG ABBRECHEN - - - + + + Finalizing... Finalisieren... - + Next Weiter - + Select this button to start writing the image Klicken Sie auf diesen Knopf, um mit dem Schreiben zu beginnen @@ -632,22 +841,22 @@ Klicken Sie auf diesen Knopf, um zu den erweiterten Einstellungen zu gelangen. - + Using custom repository: %1 Verwende benutzerdefiniertes Repository: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Tastaturnavigation: <Tab> zum nächsten Knopf navigieren <Leertaste> Knopf drücken/Element auswählen <Pfeil hoch/runter> in Listen nach oben/unten gehen - + Language: Sprache: - + Keyboard: Tastatur: @@ -656,84 +865,73 @@ Pi Modell: - [ All ] - [ Alle ] + [ Alle ] - Back - Zurück + Zurück - Go back to main menu - Zurück zum Hauptmenü + Zurück zum Hauptmenü - Released: %1 - Veröffentlicht: %1 + Veröffentlicht: %1 - Cached on your computer - Auf Ihrem Computer zwischengespeichert + Auf Ihrem Computer zwischengespeichert - Local file - Lokale Datei + Lokale Datei - Online - %1 GB download - Online - %1 GB Download + Online - %1 GB Download - - - Mounted as %1 - Als %1 eingebunden + Als %1 eingebunden - [WRITE PROTECTED] - [SCHREIBGESCHÜTZT] + [SCHREIBGESCHÜTZT] - + Are you sure you want to quit? Sind Sie sicher, dass Sie beenden möchten? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Der Raspberry Pi Imager ist noch beschäftigt. <br>Möchten Sie wirklich beenden? - + Warning Warnung - + Preparing to write... Schreiben wird vorbereitet... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Alle vorhandenen Daten auf '%1' werden gelöscht.<br>Möchten Sie wirklich fortfahren? - + Update available Update verfügbar - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Eine neuere Version von Imager ist verfügbar.<br>Möchten Sie die Webseite besuchen, um das Update herunterzuladen? @@ -742,75 +940,72 @@ Fehler beim Herunterladen der Betriebssystemsliste aus dem Internet - + Writing... %1% Schreiben... %1% - + Verifying... %1% Verifizieren... %1% - + Preparing to write... (%1) Schreiben wird vorbereitet... (%1) - + Error Fehler - + Write Successful Schreiben erfolgreich - - + + Erase Löschen - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> wurde geleert<br><br>Sie können die SD-Karte nun aus dem Lesegerät entfernen - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> wurde auf <b>%2</b> geschrieben<br><br>Sie können die SD-Karte nun aus dem Lesegerät entfernen - Error parsing os_list.json - Fehler beim Parsen von os_list.json + Fehler beim Parsen von os_list.json - + Format card as FAT32 Karte als FAT32 formatieren - + Use custom Eigenes Image - + Select a custom .img from your computer Wählen Sie eine eigene .img-Datei von Ihrem Computer - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Verbinden Sie zuerst einen USB-Stick mit Images.<br>Die Images müssen sich im Wurzelverzeichnes des USB-Sticks befinden. + Verbinden Sie zuerst einen USB-Stick mit Images.<br>Die Images müssen sich im Wurzelverzeichnes des USB-Sticks befinden. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - Die Speicherkarte ist schreibgeschützt.<br>Schieben Sie den Schutzschalter auf der linken Seite nach oben, und versuchen Sie es erneut. + Die Speicherkarte ist schreibgeschützt.<br>Schieben Sie den Schutzschalter auf der linken Seite nach oben, und versuchen Sie es erneut. Select this button to change the destination SD card diff --git a/src/i18n/rpi-imager_en.ts b/src/i18n/rpi-imager_en.ts index 016684b5..0d73d0e6 100644 --- a/src/i18n/rpi-imager_en.ts +++ b/src/i18n/rpi-imager_en.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 - + Error mounting FAT32 partition - + Operating system did not mount FAT32 partition - + Error changing to directory '%1' @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive - + opening drive - + Error running diskpart: %1 - + Error removing existing partitions - + Authentication cancelled - + Error running authopen to gain access to disk device '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). - + Cannot open storage device '%1'. - + discarding existing data on drive - + zeroing out first and last MB of drive - + Write error while zero'ing out MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). - + starting download - + Error downloading: %1 - + Access denied error while writing file to disk. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. - + Error writing file to disk - + Download corrupt. Hash does not match - - + + Error writing to storage (while flushing) - - + + Error writing to storage (while fsync) - + Error writing first block (partition table) - + Error reading from storage.<br>SD card may be broken. - + Verifying write failed. Contents of SD card is different from what was written to it. - + Customizing image Customising OS @@ -210,40 +210,105 @@ + + DstPopup + + + Storage + + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + + + + + GB + + + + + [WRITE PROTECTED] + + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + + + + + HWListModel + + + CHOOSE DEVICE + + + + + HwPopup + + + Raspberry Pi Device + + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. - + Downloading and writing image - + Select image - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file - + Error opening image file @@ -264,163 +329,233 @@ MsgPopup - + NO - + YES - + CONTINUE - + QUIT - OptionsPopup + OSListModel - - OS Customization - OS Customisation + + Recommended + + + + OSPopup - OS customization options - OS customisation options + + Operating System + - - General + + Back - - Services + + Go back to main menu - - Options + + Released: %1 + + + + + Cached on your computer + + + + + Local file - + + Online - %1 GB download + + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + + + + + OptionsGeneralTab + + Set hostname: - + Set username and password - + Username: - - + + Password: - + Configure wireless LAN - + SSID: - + Show password - + Hidden SSID - + Wireless LAN country: - + Set locale settings - + Time zone: - + Keyboard layout: + + + OptionsMiscTab - - Enable SSH + + Play sound when finished - - Use password authentication + + Eject media when finished - - Allow public-key authentication only + + Enable telemetry + + + OptionsPopup - - Set authorized_keys for '%1': + + OS Customization + OS Customisation + + + OS customization options + OS customisation options + + + + General - - RUN SSH-KEYGEN + + Services - - Play sound when finished + + Options - - Eject media when finished + + SAVE + + + OptionsServicesTab - - Enable telemetry + + Enable SSH - - SAVE + + Use password authentication + + + + + Allow public-key authentication only + + + + + Set authorized_keys for '%1': + + + + + Delete Key + + + + + RUN SSH-KEYGEN + + + + + Add SSH Key QObject - + Internal SD card reader @@ -428,32 +563,31 @@ UseSavedSettingsPopup - Use OS customization? - Use OS customisation? + Use OS customisation? - + Would you like to apply OS customization settings? Would you like to apply OS customisation settings? - + NO - + NO, CLEAR SETTINGS - + YES - + EDIT SETTINGS @@ -461,275 +595,200 @@ main - + Raspberry Pi Imager v%1 - - + Raspberry Pi Device - - CHOOSE DEVICE - - - - + Select this button to choose your target Raspberry Pi - - + Operating System - - + + CHOOSE OS - + Select this button to change the operating system - - + Storage - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE - + Select this button to change the destination storage device - + CANCEL WRITE - - + + Cancelling... - + CANCEL VERIFY - - - + + + Finalizing... - + Next - + Select this button to start writing the image - + Using custom repository: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists - + Language: - + Keyboard: - - [ All ] - - - - - Back - - - - - Go back to main menu - - - - - Released: %1 - - - - - Cached on your computer - - - - - Local file - - - - - Online - %1 GB download - - - - - - - Mounted as %1 - - - - - [WRITE PROTECTED] - - - - + Are you sure you want to quit? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? - + Warning - + Preparing to write... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? - + Update available - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? - + Writing... %1% - + Verifying... %1% - + Preparing to write... (%1) - + Error - + Write Successful - - + + Erase - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader - - Error parsing os_list.json - - - - + Format card as FAT32 - + Use custom - + Select a custom .img from your computer - - - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - - - - - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - - diff --git a/src/i18n/rpi-imager_es.ts b/src/i18n/rpi-imager_es.ts index d1b5d1eb..55191ecf 100644 --- a/src/i18n/rpi-imager_es.ts +++ b/src/i18n/rpi-imager_es.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Error extrayendo el archivo: %1 - + Error mounting FAT32 partition Error montando la partición FAT32 - + Operating system did not mount FAT32 partition El sistema operativo no montó la partición FAT32 - + Error changing to directory '%1' Error cambiando al directorio '%1' @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive desmontando unidad - + opening drive abriendo unidad - + Error running diskpart: %1 Error ejecutando diskpart: %1 - + Error removing existing partitions Error eliminando las particiones existentes - + Authentication cancelled Autenticación cancelada - + Error running authopen to gain access to disk device '%1' Error ejecutando authopen para acceder al dispositivo de disco '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Por favor, compruebe si 'Raspberry Pi Imager' tiene permitido el acceso a 'volúmenes extraíbles' en los ajustes de privacidad (en 'archivos y carpetas' o alternativamente dele 'acceso total al disco'). - + Cannot open storage device '%1'. No se puede abrir el dispositivo de almacenamiento '%1'. - + discarding existing data on drive descartando datos existentes en la unidad - + zeroing out first and last MB of drive poniendo a cero el primer y el último MB de la unidad - + Write error while zero'ing out MBR Error de escritura al poner a cero MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Error de escritura al intentar poner a cero la última parte de la tarjeta.<br>La tarjeta podría estar anunciando una capacidad incorrecta (posible falsificación). - + starting download iniciando descarga - + Error downloading: %1 Error descargando: %1 - + Access denied error while writing file to disk. Error de acceso denegado escribiendo el archivo en el disco. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. El acceso controlado a carpetas parece estar activado. Añada rpi-imager.exe y fat32format.exe a la lista de aplicaciones permitidas y vuelva a intentarlo. - + Error writing file to disk Error escribiendo el archivo en el disco - + Download corrupt. Hash does not match Descarga corrupta. El hash no coincide - - + + Error writing to storage (while flushing) Error escribiendo en la memoria (durante la limpieza) - - + + Error writing to storage (while fsync) Error escribiendo en el almacenamiento (mientras fsync) - + Error writing first block (partition table) Error escribiendo el primer bloque (tabla de particiones) - + Error reading from storage.<br>SD card may be broken. Error leyendo del almacenamiento.<br>La tarjeta SD puede estar rota. - + Verifying write failed. Contents of SD card is different from what was written to it. Error verificando la escritura. El contenido de la tarjeta SD es diferente del que se escribió en ella. - + Customizing image Personalizando imagen @@ -210,40 +210,105 @@ Formateo no implementado para esta plataforma + + DstPopup + + + Storage + Almacenamiento + + + + No storage devices found + No se han encontrado dispositivos de almacenamiento + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Montado como %1 + + + + GB + + + + + [WRITE PROTECTED] + [PROTEGIDO CONTRA ESCRITURA] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + La tarjeta SD está protegida contra escritura.<br>Pulse hacia arriba el interruptor de bloqueo situado en el lado izquierdo de la tarjeta y vuelva a intentarlo. + + + + HWListModel + + + CHOOSE DEVICE + ELEGIR DISPOSITIVO + + + + HwPopup + + + Raspberry Pi Device + Dispositivo Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. La capacidad de almacenamiento no es lo suficientemente grande.<br>Necesita ser de al menos %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. El archivo de entrada no es una imagen de disco válida.<br>El tamaño del archivo %1 bytes no es múltiplo de 512 bytes. - + Downloading and writing image Descargando y escribiendo imagen - + Select image Seleccionar imagen - + Error synchronizing time. Trying again in 3 seconds Error sincronizando la hora. Vuelva a intentarlo en 3 segundos - + STP is enabled on your Ethernet switch. Getting IP will take long time. El comutador de Ethernet tiene el STP activado. La obtención de la IP puede tardar mucho. - + Would you like to prefill the wifi password from the system keychain? ¿Desea rellenar previamente la contraseña wifi desde el llavero del sistema? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file abriendo archivo de imagen - + Error opening image file Error abriendo archivo de imagen @@ -264,159 +329,309 @@ MsgPopup - + NO NO - + YES - + CONTINUE CONTINUAR - + QUIT SALIR + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Sistema operativo + + + + Back + Volver + + + + Go back to main menu + Volver al menú principal + + + + Released: %1 + Publicado: %1 + + + + Cached on your computer + En caché en su ordenador + + + + Local file + Archivo local + + + + Online - %1 GB download + En línea - descarga de %1 GB + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Conecte primero una memoria USB que contenga imágenes.<br>Las imágenes deben estar ubicadas en la carpeta raíz de la memoria USB. + + + + OptionsGeneralTab + + + Set hostname: + Establecer nombre de anfitrión: + + + + Set username and password + Establecer nombre de usuario y contraseña + + + + Username: + Nombre de usuario: + + + + + Password: + Contraseña: + + + + Configure wireless LAN + Configurar LAN inalámbrica + + + + SSID: + SSID: + + + + Show password + Mostrar contraseña + + + + Hidden SSID + SSID oculta + + + + Wireless LAN country: + País de LAN inalámbrica: + + + + Set locale settings + Establecer ajustes regionales + + + + Time zone: + Zona horaria: + + + + Keyboard layout: + Distribución del teclado: + + + + OptionsMiscTab + + + Play sound when finished + Reproducir sonido al finalizar + + + + Eject media when finished + Expulsar soporte al finalizar + + + + Enable telemetry + Activar telemetría + + OptionsPopup - + OS Customization Personalización del SO - + General General - + Services Servicios - + Options Opciones - Set hostname: - Establecer nombre de anfitrión: + Establecer nombre de anfitrión: - Set username and password - Establecer nombre de usuario y contraseña + Establecer nombre de usuario y contraseña - Username: - Nombre de usuario: + Nombre de usuario: - - Password: - Contraseña: + Contraseña: - Configure wireless LAN - Configurar LAN inalámbrica + Configurar LAN inalámbrica - SSID: - SSID: + SSID: - Show password - Mostrar contraseña + Mostrar contraseña - Hidden SSID - SSID oculta + SSID oculta - Wireless LAN country: - País de LAN inalámbrica: + País de LAN inalámbrica: - Set locale settings - Establecer ajustes regionales + Establecer ajustes regionales - Time zone: - Zona horaria: + Zona horaria: - Keyboard layout: - Distribución del teclado: + Distribución del teclado: - Enable SSH - Activar SSH + Activar SSH - Use password authentication - Usar autenticación por contraseña + Usar autenticación por contraseña - Allow public-key authentication only - Permitir solo la autenticación de clave pública + Permitir solo la autenticación de clave pública - Set authorized_keys for '%1': - Establecer authorized_keys para '%1': + Establecer authorized_keys para '%1': - RUN SSH-KEYGEN - EJECUTAR SSH-KEYGEN + EJECUTAR SSH-KEYGEN - Play sound when finished - Reproducir sonido al finalizar + Reproducir sonido al finalizar - Eject media when finished - Expulsar soporte al finalizar + Expulsar soporte al finalizar - Enable telemetry - Activar telemetría + Activar telemetría - + SAVE GUARDAR + + OptionsServicesTab + + + Enable SSH + Activar SSH + + + + Use password authentication + Usar autenticación por contraseña + + + + Allow public-key authentication only + Permitir solo la autenticación de clave pública + + + + Set authorized_keys for '%1': + Establecer authorized_keys para '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + EJECUTAR SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Lector de tarjetas SD interno @@ -424,32 +639,31 @@ UseSavedSettingsPopup - Use OS customization? - ¿Usar la personalización del SO? + ¿Usar la personalización del SO? - + Would you like to apply OS customization settings? ¿Desea aplicar los ajustes de personalización del SO? - + NO NO - + NO, CLEAR SETTINGS NO, BORRAR AJUSTES - + YES - + EDIT SETTINGS EDITAR AJUSTES @@ -457,275 +671,256 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Dispositivo Raspberry Pi - CHOOSE DEVICE - ELEGIR DISPOSITIVO + ELEGIR DISPOSITIVO - + Select this button to choose your target Raspberry Pi Seleccione este botón para elegir su Raspberry Pi objetivo - - + Operating System Sistema operativo - - + + CHOOSE OS ELEGIR SO - + Select this button to change the operating system Seleccione este botón para cambiar el sistema operativo - - + Storage Almacenamiento - + Network not ready yet La red aún no está lista - No storage devices found - No se han encontrado dispositivos de almacenamiento + No se han encontrado dispositivos de almacenamiento - - + + CHOOSE STORAGE ELEGIR ALMACENAMIENTO - + Select this button to change the destination storage device Seleccione este botón para cambiar el dispositivo de almacenamiento de destino - + CANCEL WRITE CANCELAR ESCRITURA - - + + Cancelling... Cancelando... - + CANCEL VERIFY CANCELAR VERIFICACIÓN - - - + + + Finalizing... Finalizando... - + Next Siguiente - + Select this button to start writing the image Seleccione este botón para empezar a escribir la imagen - + Using custom repository: %1 Usando repositorio personalizado: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Navegación por teclado: <tab> navegar al botón siguiente <space> pulsar botón/seleccionar elemento <arrow up/down> subir/bajar en listas - + Language: Idioma: - + Keyboard: Teclado: - [ All ] - [ Todos ] + [ Todos ] - Back - Volver + Volver - Go back to main menu - Volver al menú principal + Volver al menú principal - Released: %1 - Publicado: %1 + Publicado: %1 - Cached on your computer - En caché en su ordenador + En caché en su ordenador - Local file - Archivo local + Archivo local - Online - %1 GB download - En línea - descarga de %1 GB + En línea - descarga de %1 GB - - - Mounted as %1 - Montado como %1 + Montado como %1 - [WRITE PROTECTED] - [PROTEGIDO CONTRA ESCRITURA] + [PROTEGIDO CONTRA ESCRITURA] - + Are you sure you want to quit? ¿Está seguro de que quiere salir? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager sigue ocupado.<br>¿Está seguro de que quiere salir? - + Warning Advertencia - + Preparing to write... Preparando para escribir... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Se borrarán todos los datos existentes en '%1'.<br>¿Está seguro de que desea continuar? - + Update available Actualización disponible - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Hay una versión más reciente de Imager disponible.<br>¿Desea visitar el sitio web para descargarla? - + Writing... %1% Escribiendo... %1% - + Verifying... %1% Verificando... %1% - + Preparing to write... (%1) Preparando para escribir... (%1) - + Error Error - + Write Successful Escritura exitosa - - + + Erase Borrar - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> se ha borrado<br><br>Ya puede retirar la tarjeta SD del lector - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> se ha escrito en <b>%2</b><br><br>Ya puede retirar la tarjeta SD del lector - Error parsing os_list.json - Error al parsear os_list.json + Error al parsear os_list.json - + Format card as FAT32 Formatear tarjeta como FAT32 - + Use custom Usar personalizado - + Select a custom .img from your computer Seleccione un .img personalizado de su ordenador - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Conecte primero una memoria USB que contenga imágenes.<br>Las imágenes deben estar ubicadas en la carpeta raíz de la memoria USB. + Conecte primero una memoria USB que contenga imágenes.<br>Las imágenes deben estar ubicadas en la carpeta raíz de la memoria USB. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - La tarjeta SD está protegida contra escritura.<br>Pulse hacia arriba el interruptor de bloqueo situado en el lado izquierdo de la tarjeta y vuelva a intentarlo. + La tarjeta SD está protegida contra escritura.<br>Pulse hacia arriba el interruptor de bloqueo situado en el lado izquierdo de la tarjeta y vuelva a intentarlo. diff --git a/src/i18n/rpi-imager_fr.ts b/src/i18n/rpi-imager_fr.ts index 7964aa69..15ace3a0 100644 --- a/src/i18n/rpi-imager_fr.ts +++ b/src/i18n/rpi-imager_fr.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Erreur lors de l'extraction de l'archive : %1 - + Error mounting FAT32 partition Erreur lors du montage de la partition FAT32 - + Operating system did not mount FAT32 partition Le système d'exploitation n'a pas monté la partition FAT32 - + Error changing to directory '%1' Erreur lors du changement du répertoire '%1' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive démontage du disque - + opening drive ouverture du disque - + Error running diskpart: %1 Erreur lors de l'exécution de diskpart : %1 - + Error removing existing partitions Erreur lors de la suppression des partitions existantes - + Authentication cancelled Authentification annulée - + Error running authopen to gain access to disk device '%1' Erreur lors de l'exécution d'authopen pour accéder au périphérique du stockage '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Veuillez vérifier dans les réglages de confidentialité (sous 'fichiers et dossiers') si 'Raspberry Pi Imager' est autorisé à accéder aux volumes amovibles (ou bien donnez-lui accès complet au disque). - + Cannot open storage device '%1'. Impossible d'ouvrir le périphérique de stockage '%1'. - + discarding existing data on drive suppression des données existantes sur le disque - + zeroing out first and last MB of drive mise à zéro du premier et du dernier Mo du disque - + Write error while zero'ing out MBR Erreur d'écriture lors du formatage du MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Erreur d'écriture lors de la tentative de formatage de la dernière partie de la carte.<br>La carte annonce peut-être une capacité erronée (contrefaçon possible). - + starting download début du téléchargement - + Error downloading: %1 Erreur de téléchargement : %1 - + Access denied error while writing file to disk. Accès refusé lors de l'écriture d'un fichier sur le disque. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. L'accès contrôlé aux dossiers semble être activé. Veuillez ajouter rpi-imager.exe et fat32format.exe à la liste des applications autorisées et réessayez. - + Error writing file to disk Erreur d'écriture de fichier sur le disque - + Download corrupt. Hash does not match Téléchargement corrompu. La signature ne correspond pas - - + + Error writing to storage (while flushing) Erreur d'écriture dans le stockage (lors du formatage) - - + + Error writing to storage (while fsync) Erreur d'écriture dans le stockage (pendant l'exécution de fsync) - + Error writing first block (partition table) Erreur lors de l'écriture du premier bloc (table de partition) - + Error reading from storage.<br>SD card may be broken. Erreur de lecture du stockage.<br>La carte SD est peut-être défectueuse. - + Verifying write failed. Contents of SD card is different from what was written to it. La vérification de l'écriture à échoué. Le contenu de la carte SD est différent de ce qui y a été écrit. - + Customizing image Personnalisation de l'image @@ -250,40 +250,105 @@ Formatage non implémenté pour cette plateforme + + DstPopup + + + Storage + Stockage + + + + No storage devices found + Aucun périphérique de stockage trouvé + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Monté sur %1 + + + + GB + + + + + [WRITE PROTECTED] + [PROTÉGÉ EN ÉCRITURE] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + La carte SD est protégée en écriture.<br>Poussez vers le haut le commutateur de verrouillage sur le côté gauche de la carte et essayez à nouveau. + + + + HWListModel + + + CHOOSE DEVICE + CHOISIR LE MODÈLE + + + + HwPopup + + + Raspberry Pi Device + Modèle de Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. La capacité de stockage n'est pas assez grande.<br>Elle doit être d'au moins %1 Go. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Le fichier source n'est pas une image disque valide.<br>La taille du fichier (d'%1 octets) n'est pas un multiple de 512 octets. - + Downloading and writing image Téléchargement et écriture de l'image - + Select image Sélectionner l'image - + Error synchronizing time. Trying again in 3 seconds Erreur de synchronisation de l'heure. Nouvelle tentative dans 3 secondes - + STP is enabled on your Ethernet switch. Getting IP will take long time. Le protocole STP est activé sur votre commutateur Ethernet. L'obtention de l'adresse IP prendra beaucoup de temps. - + Would you like to prefill the wifi password from the system keychain? Voulez-vous pré-remplir le mot de passe Wi-Fi à partir du trousseau du système ? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file ouverture de l'image disque - + Error opening image file Erreur lors de l'ouverture de l'image disque @@ -304,30 +369,163 @@ MsgPopup - + NO NON - + YES OUI - + CONTINUE CONTINUER - + QUIT QUITTER + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Système d'exploitation + + + + Back + Retour + + + + Go back to main menu + Retour au menu principal + + + + Released: %1 + Publié le : %1 + + + + Cached on your computer + Mis en cache sur votre ordinateur + + + + Local file + Fichier local + + + + Online - %1 GB download + En ligne - %1 GO à télécharger + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Connecter d'abord une clé USB contenant les images.<br>Les images doivent se trouver dans le dossier racine de la clé USB. + + + + OptionsGeneralTab + + + Set hostname: + Nom d'hôte + + + + Set username and password + Définir nom d'utilisateur et mot de passe + + + + Username: + Nom d'utilisateur : + + + + + Password: + Mot de passe : + + + + Configure wireless LAN + Configurer le Wi-Fi + + + + SSID: + SSID : + + + + Show password + Afficher le mot de passe + + + + Hidden SSID + SSID caché + + + + Wireless LAN country: + Pays Wi-Fi : + + + + Set locale settings + Définir les réglages locaux + + + + Time zone: + Fuseau horaire : + + + + Keyboard layout: + Type de clavier : + + + + OptionsMiscTab + + + Play sound when finished + Jouer un son quand terminé + + + + Eject media when finished + Éjecter le média quand terminé + + + + Enable telemetry + Activer la télémétrie + + OptionsPopup - + OS Customization Personnalisation de l'OS @@ -344,123 +542,102 @@ pour toutes les sessions - + General Général - + Services Services - + Options Options - Set hostname: - Nom d'hôte + Nom d'hôte - Set username and password - Définir nom d'utilisateur et mot de passe + Définir nom d'utilisateur et mot de passe - Username: - Nom d'utilisateur : + Nom d'utilisateur : - - Password: - Mot de passe : + Mot de passe : - Configure wireless LAN - Configurer le Wi-Fi + Configurer le Wi-Fi - SSID: - SSID : + SSID : - Show password - Afficher le mot de passe + Afficher le mot de passe - Hidden SSID - SSID caché + SSID caché - Wireless LAN country: - Pays Wi-Fi : + Pays Wi-Fi : - Set locale settings - Définir les réglages locaux + Définir les réglages locaux - Time zone: - Fuseau horaire : + Fuseau horaire : - Keyboard layout: - Type de clavier : + Type de clavier : - Enable SSH - Activer SSH + Activer SSH - Use password authentication - Utiliser un mot de passe pour l'authentification + Utiliser un mot de passe pour l'authentification - Allow public-key authentication only - Authentification via clef publique + Authentification via clef publique - Set authorized_keys for '%1': - Définir authorized_keys pour '%1' : + Définir authorized_keys pour '%1' : - RUN SSH-KEYGEN - LANCER SSH-KEYGEN + LANCER SSH-KEYGEN - Play sound when finished - Jouer un son quand terminé + Jouer un son quand terminé - Eject media when finished - Éjecter le média quand terminé + Éjecter le média quand terminé - Enable telemetry - Activer la télémétrie + Activer la télémétrie - + SAVE ENREGISTRER @@ -469,10 +646,48 @@ Réglages permanents + + OptionsServicesTab + + + Enable SSH + Activer SSH + + + + Use password authentication + Utiliser un mot de passe pour l'authentification + + + + Allow public-key authentication only + Authentification via clef publique + + + + Set authorized_keys for '%1': + Définir authorized_keys pour '%1' : + + + + Delete Key + + + + + RUN SSH-KEYGEN + LANCER SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Lecteur de carte SD interne @@ -480,32 +695,31 @@ UseSavedSettingsPopup - Use OS customization? - Utiliser la personnalisation de l'OS ? + Utiliser la personnalisation de l'OS ? - + Would you like to apply OS customization settings? Voulez-vous appliquer les réglages de personnalisation de l'OS ? - + NO NON - + NO, CLEAR SETTINGS NON, EFFACER LES RÉGLAGES - + YES OUI - + EDIT SETTINGS MODIFIER RÉGLAGES @@ -513,62 +727,57 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Modèle de Raspberry Pi - CHOOSE DEVICE - CHOISIR LE MODÈLE + CHOISIR LE MODÈLE - + Select this button to choose your target Raspberry Pi Sélectionner ce bouton pour choisir le modèle de votre Raspberry Pi - - + Operating System Système d'exploitation - - + + CHOOSE OS CHOISIR L'OS - + Select this button to change the operating system Sélectionner ce bouton pour changer le système d'exploitation - - + Storage Stockage - + Network not ready yet Le réseau n'est pas encore prêt - No storage devices found - Aucun périphérique de stockage trouvé + Aucun périphérique de stockage trouvé - - + + CHOOSE STORAGE CHOISIR LE STOCKAGE @@ -577,40 +786,40 @@ ÉCRIRE - + Select this button to change the destination storage device Sélectionner ce bouton pour modifier le périphérique de stockage de destination - + CANCEL WRITE ANNULER L'ÉCRITURE - - + + Cancelling... Annulation... - + CANCEL VERIFY ANNULER LA VÉRIFICATION - - - + + + Finalizing... Finalisation... - + Next Suivant - + Select this button to start writing the image Sélectionner ce bouton pour commencer l'écriture de l'image @@ -619,22 +828,22 @@ Sélectionner ce bouton pour accéder aux réglages avancés - + Using custom repository: %1 Utilisation d'un dépôt personnalisé : %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Navigation au clavier : <tab> passer au bouton suivant <espace> presser un bouton/sélectionner un élément <flèche haut/bas> monter/descendre dans les listes - + Language: Langue : - + Keyboard: Clavier : @@ -643,84 +852,73 @@ Modèle RPi : - [ All ] - [ Tous ] + [ Tous ] - Back - Retour + Retour - Go back to main menu - Retour au menu principal + Retour au menu principal - Released: %1 - Publié le : %1 + Publié le : %1 - Cached on your computer - Mis en cache sur votre ordinateur + Mis en cache sur votre ordinateur - Local file - Fichier local + Fichier local - Online - %1 GB download - En ligne - %1 GO à télécharger + En ligne - %1 GO à télécharger - - - Mounted as %1 - Monté sur %1 + Monté sur %1 - [WRITE PROTECTED] - [PROTÉGÉ EN ÉCRITURE] + [PROTÉGÉ EN ÉCRITURE] - + Are you sure you want to quit? Voulez-vous vraiment quitter ? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager est encore occupé.<br>Voulez-vous vraiment quitter ? - + Warning Attention - + Preparing to write... Préparation de l'écriture... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Toutes les données sur le périphérique de stockage '%1' vont être supprimées.<br>Voulez-vous vraiment continuer ? - + Update available Mise à jour disponible - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Une version plus récente d'Imager est disponible.<br>Voulez-vous accéder au site web pour la télécharger ? @@ -729,75 +927,72 @@ Erreur lors du téléchargement de la liste des systèmes d'exploitation à partir d'Internet - + Writing... %1% Écriture... %1% - + Verifying... %1% Vérification... %1% - + Preparing to write... (%1) Préparation de l'écriture... (%1) - + Error Erreur - + Write Successful Écriture réussie - - + + Erase Effacer - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> a bien été effacé<br><br>Vous pouvez retirer la carte SD du lecteur - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> a bien été écrit sur <b>%2</b><br><br>Vous pouvez retirer la carte SD du lecteur - Error parsing os_list.json - Erreur de lecture du fichier os_list.json + Erreur de lecture du fichier os_list.json - + Format card as FAT32 Formater la carte SD en FAT32 - + Use custom Utiliser image personnalisée - + Select a custom .img from your computer Sélectionner une image disque personnalisée (.img) sur votre ordinateur - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Connecter d'abord une clé USB contenant les images.<br>Les images doivent se trouver dans le dossier racine de la clé USB. + Connecter d'abord une clé USB contenant les images.<br>Les images doivent se trouver dans le dossier racine de la clé USB. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - La carte SD est protégée en écriture.<br>Poussez vers le haut le commutateur de verrouillage sur le côté gauche de la carte et essayez à nouveau. + La carte SD est protégée en écriture.<br>Poussez vers le haut le commutateur de verrouillage sur le côté gauche de la carte et essayez à nouveau. Select this button to change the destination SD card diff --git a/src/i18n/rpi-imager_he.ts b/src/i18n/rpi-imager_he.ts index ac154edd..b3dd99d0 100644 --- a/src/i18n/rpi-imager_he.ts +++ b/src/i18n/rpi-imager_he.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 שגיאה בחילוץ ארכיון: %1 - + Error mounting FAT32 partition שגיאה בעיגון מחיצת FAT32 - + Operating system did not mount FAT32 partition מערכת ההפעלה לא עיגנה מחיצת FAT32 - + Error changing to directory '%1' שגיאה במעבר לתיקייה ‚%1’ @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive עיגון הכונן מנותק - + opening drive הכונן נפתח - + Error running diskpart: %1 שגיאה בהרצת diskpart‏: %1 - + Error removing existing partitions שגיאה בהסרת מחיצות קיימות - + Authentication cancelled האימות בוטל - + Error running authopen to gain access to disk device '%1' שגיאה בהרצת authopen לקבלת גישה להתקן הכונן ‚%1’ - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). נא לוודא ש־‚Raspberry Pi Imager (צורב קובצי דמות)’ מורשה לגשת ל‚כרכים נתיקים' בהגדרות הפרטיות (תחת ‚קבצים ותיקיות’ או לחלופין להעניק לו ‚גישה מלאה לכוננים’). - + Cannot open storage device '%1'. לא ניתן לפתוח את התקן האחסון ‚%1’. - + discarding existing data on drive הדחת הנתונים הקיימים שבכונן - + zeroing out first and last MB of drive מילוי המגה בתים הראשון והאחרון בכונן באפסים - + Write error while zero'ing out MBR שגיאת כתיבה במהלך כתיבת אפסים ל־MBR (רשומת טעינה עיקרית) - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). שגיאת צריבה במהלך הניסיון למלא את החלק הסופי של הכרטיס באפסים.<br>יכול להיות שהכרטיס מפרסם על עצמו נפח שגוי (כנראה זיוף). - + starting download ההורדה מתחילה - + Error downloading: %1 שגיאת הורדה: %1 - + Access denied error while writing file to disk. הגישה נדחתה תוך כדי הצריבה לכונן. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. נראה שגישה מבוקרת לתיקיות פפעילה. נא להוסיף את rpi-imager.exe ואת fat32format.exe לרשימת היישומים המורשים ולנסות שוב. - + Error writing file to disk שגיאה בצריבת קובץ לכונן - + Download corrupt. Hash does not match ההורדה נפגמה. הגיבוב לא תואם - - + + Error writing to storage (while flushing) שגיאת צריבה לאחסון (במהלך מחיקה מוחלטת) - - + + Error writing to storage (while fsync) שגיאת צריבה לאחסון (במהלך fsync) - + Error writing first block (partition table) שגיאה בכתיבה לבלוק הראשון (טבלת מחיצות) - + Error reading from storage.<br>SD card may be broken. שגיאת קריאה מאחסון.<br>כנראה שכרטיס ה־SD פגום. - + Verifying write failed. Contents of SD card is different from what was written to it. וידוא הצריבה נכשל. תוכן כרטיס ה־SD שונה ממה שלכאורה נצרב אליו. - + Customizing image הדמות מותאמת אישית @@ -210,40 +210,105 @@ פרמוט לא מומש לפלטפורמה הזאת + + DstPopup + + + Storage + אחסון + + + + No storage devices found + לאנמצאו התקני אחסון + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + מעוגן בתור %1 + + + + GB + + + + + [WRITE PROTECTED] + [מוגן מפני צריבה] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + כרטיס ה־SD מוגן מפני כתיבה.<br>נא ללחוץ על מתג הנעילה בצד השמאלי של הכרטיס כלפי מעלה ולנסות שוב. + + + + HWListModel + + + CHOOSE DEVICE + נא לבחור מכשיר + + + + HwPopup + + + Raspberry Pi Device + מכשיר Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. נפל האחסון לא גדול מספיק.<br>חייב להיות %1 ג״ב לפחות. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. קובץ הקלט אינו דמות כונן תקנית.<br>גודל הקובץ על סך %1 בתים הוא לא מכפלה של 512 בתים. - + Downloading and writing image הורדה וצריבה של דמות - + Select image נא לבחור דמות - + Error synchronizing time. Trying again in 3 seconds שגיאה בסנכרון השעון. יתבצע ניסיון חוזר עוד 3 שניות - + STP is enabled on your Ethernet switch. Getting IP will take long time. STP (פרוטוקול העץ הפורש) פעיל בציוד התקשורת שלך. קבלת כתובת IP תארוך זמן רב. - + Would you like to prefill the wifi password from the system keychain? למלא את סיסמת הרשת האלחוטית מראש מצרור המפתחות של המערכת? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file פתיחת קובץ דמות - + Error opening image file שגיאה בפתיחת קובץ דמות @@ -264,30 +329,163 @@ MsgPopup - + NO לא - + YES כן - + CONTINUE להמשיך - + QUIT יציאה + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + מערכת הפעלה + + + + Back + חזרה + + + + Go back to main menu + חזרה לתפריט הראשי + + + + Released: %1 + מועד יציאה לאור: %1 + + + + Cached on your computer + שמור במטמון במחשב שלך + + + + Local file + קובץ מקומי + + + + Online - %1 GB download + מקוון - הורדה של %1 ג״ב + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + נא לחבר קודם כונן USB שמכיל קובצי דמות.<br>קובצי הדמות חייבים להימצא בתיקייה הראשית של כונן ה־USB. + + + + OptionsGeneralTab + + + Set hostname: + הגדרת שם מארח: + + + + Set username and password + הגדרת שם משתמש וסיסמה + + + + Username: + שם משתמש: + + + + + Password: + סיסמה: + + + + Configure wireless LAN + הגדרת רשת אלחוטית + + + + SSID: + שם רשת (SSID): + + + + Show password + הצגת סיסמה + + + + Hidden SSID + רשת מוסתרת (BSSID) + + + + Wireless LAN country: + מדינת רשת אלחוטית: + + + + Set locale settings + הגדרת תצורה מקומית + + + + Time zone: + אזור זמן: + + + + Keyboard layout: + פריסת מקלדת: + + + + OptionsMiscTab + + + Play sound when finished + להשמיע צליל עם הסיום + + + + Eject media when finished + להוציא את אמצעי האחסון עם סיום הפעולה + + + + Enable telemetry + הפעלת מדדים + + OptionsPopup - + OS Customization התאמת מערכת הפעלה @@ -296,131 +494,148 @@ אפשרויות התאמת מערכת הפעלה - + General כללי - + Services שירותים - + Options אפשרויות - Set hostname: - הגדרת שם מארח: + הגדרת שם מארח: - Set username and password - הגדרת שם משתמש וסיסמה + הגדרת שם משתמש וסיסמה - Username: - שם משתמש: + שם משתמש: - - Password: - סיסמה: + סיסמה: - Configure wireless LAN - הגדרת רשת אלחוטית + הגדרת רשת אלחוטית - SSID: - שם רשת (SSID): + שם רשת (SSID): - Show password - הצגת סיסמה + הצגת סיסמה - Hidden SSID - רשת מוסתרת (BSSID) + רשת מוסתרת (BSSID) - Wireless LAN country: - מדינת רשת אלחוטית: + מדינת רשת אלחוטית: - Set locale settings - הגדרת תצורה מקומית + הגדרת תצורה מקומית - Time zone: - אזור זמן: + אזור זמן: - Keyboard layout: - פריסת מקלדת: + פריסת מקלדת: - Enable SSH - הפעלת SSH + הפעלת SSH - Use password authentication - להשתמש באימות עם סיסמה + להשתמש באימות עם סיסמה - Allow public-key authentication only - לאפשר אימות עם מפתח ציבורי בלבד + לאפשר אימות עם מפתח ציבורי בלבד - Set authorized_keys for '%1': - להגדיר authorized_keys (מפתחות מורשים) עבור ‚%1’: + להגדיר authorized_keys (מפתחות מורשים) עבור ‚%1’: - RUN SSH-KEYGEN - להריץ את SSH-KEYGEN + להריץ את SSH-KEYGEN - Play sound when finished - להשמיע צליל עם הסיום + להשמיע צליל עם הסיום - Eject media when finished - להוציא את אמצעי האחסון עם סיום הפעולה + להוציא את אמצעי האחסון עם סיום הפעולה - Enable telemetry - הפעלת מדדים + הפעלת מדדים - + SAVE שמירה + + OptionsServicesTab + + + Enable SSH + הפעלת SSH + + + + Use password authentication + להשתמש באימות עם סיסמה + + + + Allow public-key authentication only + לאפשר אימות עם מפתח ציבורי בלבד + + + + Set authorized_keys for '%1': + להגדיר authorized_keys (מפתחות מורשים) עבור ‚%1’: + + + + Delete Key + + + + + RUN SSH-KEYGEN + להריץ את SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader קורא כרטיסי SD פנימי @@ -428,32 +643,31 @@ UseSavedSettingsPopup - Use OS customization? - להשתמש בהתאמת מערכת הפעלה? + להשתמש בהתאמת מערכת הפעלה? - + Would you like to apply OS customization settings? להחיל הגדרות התאמת מערכת הפעלה? - + NO לא - + NO, CLEAR SETTINGS לא, לאפס את ההגדרות - + YES כן - + EDIT SETTINGS עריכת ההגדרות @@ -461,275 +675,256 @@ main - + Raspberry Pi Imager v%1 צורב דמויות ל־Raspberry Pi גרסה %1 - - + Raspberry Pi Device מכשיר Raspberry Pi - CHOOSE DEVICE - נא לבחור מכשיר + נא לבחור מכשיר - + Select this button to choose your target Raspberry Pi נא לבחור בכפתור הזה כדי לבחור את מכשיר ה־Raspberry Pi המיועד - - + Operating System מערכת הפעלה - - + + CHOOSE OS נא לבחור מערכת הפעלה - + Select this button to change the operating system יש לבחור בכפתור הזה כדי להחליף את מערכת ההפעלה - - + Storage אחסון - + Network not ready yet הרשת לא מוכנה עדיין - No storage devices found - לאנמצאו התקני אחסון + לאנמצאו התקני אחסון - - + + CHOOSE STORAGE נא לבחור אמצעי אחסון - + Select this button to change the destination storage device יש לבחור בכפתור הזה כדי לשנות את התקן אחסון היעד - + CANCEL WRITE ביטול צריבה - - + + Cancelling... הצריבה מבוטלת… - + CANCEL VERIFY ביטול אימות - - - + + + Finalizing... הצריבה מסתיימת… - + Next הבא - + Select this button to start writing the image יש לבחור בכפתור הזה כדי להתחיל את צריבת הדמות - + Using custom repository: %1 שימוש במאגר מותאם אישית: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists ניווט מקלדת: <tab> ניווט לכפתור הבא <רווח> לחיצה על כפתור/בחירת פריט <חץ למעלה/למטה> לעלות/לרדת ברשימות - + Language: שפה: - + Keyboard: מקלדת: - [ All ] - [ הכול ] + [ הכול ] - Back - חזרה + חזרה - Go back to main menu - חזרה לתפריט הראשי + חזרה לתפריט הראשי - Released: %1 - מועד יציאה לאור: %1 + מועד יציאה לאור: %1 - Cached on your computer - שמור במטמון במחשב שלך + שמור במטמון במחשב שלך - Local file - קובץ מקומי + קובץ מקומי - Online - %1 GB download - מקוון - הורדה של %1 ג״ב + מקוון - הורדה של %1 ג״ב - - - Mounted as %1 - מעוגן בתור %1 + מעוגן בתור %1 - [WRITE PROTECTED] - [מוגן מפני צריבה] + [מוגן מפני צריבה] - + Are you sure you want to quit? לצאת? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager (צורב הדמויות) עדיין עסוק.<br>לצאת בכל זאת? - + Warning אזהרה - + Preparing to write... בהכנות לצריבה… - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? כל הנתונים שיש כרגע על ‚%1’ יימחקו.<br>להמשיך? - + Update available יש עדכון - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? יש גרסה חדשה יותר של צורב הדמויות (Imager) זמינה.<br>לבקר באתר כדי להוריד אותה? - + Writing... %1% מתבצעת צריבה… %1% - + Verifying... %1% מתבצע אימות… %1% - + Preparing to write... (%1) בהכנות לצריבה… (%1) - + Error שגיאה - + Write Successful הצריבה הצליחה - - + + Erase מחיקה - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> נמחק<br><br>אפשר להסיר את כרטיס ה־SD מהקורא - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> נכתב אל <b>%2</b><br><br>אפשר להסיר את כרטיס ה־SD מהקורא - Error parsing os_list.json - שגיאה בפענוח os_list.json + שגיאה בפענוח os_list.json - + Format card as FAT32 פרמוט הכרטיס כ־FAT32 - + Use custom להשתמש בהתאמה אישית - + Select a custom .img from your computer בחירת קובץ ‎.img מותאם מהמחשב שלך - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - נא לחבר קודם כונן USB שמכיל קובצי דמות.<br>קובצי הדמות חייבים להימצא בתיקייה הראשית של כונן ה־USB. + נא לחבר קודם כונן USB שמכיל קובצי דמות.<br>קובצי הדמות חייבים להימצא בתיקייה הראשית של כונן ה־USB. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - כרטיס ה־SD מוגן מפני כתיבה.<br>נא ללחוץ על מתג הנעילה בצד השמאלי של הכרטיס כלפי מעלה ולנסות שוב. + כרטיס ה־SD מוגן מפני כתיבה.<br>נא ללחוץ על מתג הנעילה בצד השמאלי של הכרטיס כלפי מעלה ולנסות שוב. diff --git a/src/i18n/rpi-imager_it.ts b/src/i18n/rpi-imager_it.ts index 20b92c86..b5edb4d9 100644 --- a/src/i18n/rpi-imager_it.ts +++ b/src/i18n/rpi-imager_it.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Errore estrazione archivio: %1 - + Error mounting FAT32 partition Errore montaggio partizione FAT32 - + Operating system did not mount FAT32 partition Il sistema operativo non ha montato la partizione FAT32 - + Error changing to directory '%1' Errore passaggio a cartella '%1' @@ -28,125 +28,125 @@ DownloadThread - + unmounting drive smontaggio unità - + opening drive apertura unità - + Error running diskpart: %1 Errore esecuzione diskpart: %1 - + Error removing existing partitions Errore rimozione partizioni esistenti - + Authentication cancelled Autenticazione annullata - + Error running authopen to gain access to disk device '%1' Errore esecuzione auhopen per ottenere accesso al dispositivo disco %1 - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Verifica se a 'Raspberry Pi Imager' è consentito l'accesso a 'volumi rimovibili' nelle impostazioni privacy (in 'file e cartelle' o in alternativa concedi 'accesso completo al disco'). - + Cannot open storage device '%1'. Impossibile aprire dispositivo storage '%1'. - + discarding existing data on drive elimina i dati esistenti nell'unità - + zeroing out first and last MB of drive azzera il primo e l'ultimo MB dell'unità - + Write error while zero'ing out MBR Errore scrittura durante azzeramento MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Errore di scrittura durante il tentativo di azzerare l'ultima parte della scheda.<br>La scheda potrebbe riportare una capacità maggiore di quella reale (possibile contraffazione). - + starting download avvio download - + Error downloading: %1 Errore download: %1 - + Access denied error while writing file to disk. Errore accesso negato durante la scrittura del file su disco. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Sembra sia abilitato l'accesso controllato alle cartelle. Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all'elenco delle app consentite e riprova. - + Error writing file to disk Errore scrittura file su disco - + Download corrupt. Hash does not match Download corrotto.<br>L'hash non corrisponde - - + + Error writing to storage (while flushing) Errore scrittura nello storage (durante flushing) - - + + Error writing to storage (while fsync) Errore scrittura nello storage (durante fsync) - + Error writing first block (partition table) Errore scrittura primo blocco (tabella partizione) - + Error reading from storage.<br>SD card may be broken. Errore lettura dallo storage.<br>La scheda SD potrebbe essere danneggiata. - + Verifying write failed. Contents of SD card is different from what was written to it. Verifica scrittura fallita.<br>Il contenuto della SD è differente da quello che vi è stato scritto. - + Customizing image Personalizza immagine @@ -211,40 +211,105 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos Formattazione non implementata per questa piattaforma + + DstPopup + + + Storage + Scheda SD + + + + No storage devices found + Nessun dispositivo archiviazione trovato + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Montato come %1 + + + + GB + + + + + [WRITE PROTECTED] + [PROTETTA DA SCRITTURA] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + La scheda SD è protetta da scrittura.<br>Sposta verso l'alto l'interruttore LOCK sul lato sinistro della scheda SD e riprova. + + + + HWListModel + + + CHOOSE DEVICE + SCEGLI DISPOSITIVO + + + + HwPopup + + + Raspberry Pi Device + Dispositivo Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. La capacità dello storage non è sufficiente.<br>Sono necessari almeno %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Il file sorgente non è un'immagine disco valida.<br>La dimensione file %1 non è un multiplo di 512 byte. - + Downloading and writing image Download e scrittura file immagine - + Select image Seleziona file immagine - + Error synchronizing time. Trying again in 3 seconds Errore durante la sincronizzazione dell'ora, riprova tra 3 secondi - + STP is enabled on your Ethernet switch. Getting IP will take long time. STP è abilitato sullo switch Ethernet. Ottenere l'IP richiederà molto tempo. - + Would you like to prefill the wifi password from the system keychain? Vuoi precompilare la password WiFi usando il portachiavi di sistema? @@ -252,12 +317,12 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos LocalFileExtractThread - + opening image file apertura file immagine - + Error opening image file Errore durante l'apertura del file immagine @@ -265,30 +330,163 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos MsgPopup - + NO NO - + YES SI - + CONTINUE CONTINUA - + QUIT ESCI + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Sistema operativo + + + + Back + Indietro + + + + Go back to main menu + Torna al menu principale + + + + Released: %1 + Rilasciato: %1 + + + + Cached on your computer + Memorizzato temporaneamente nel computer + + + + Local file + File locale + + + + Online - %1 GB download + Online - Download %1 GB + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Prima collega una chiavetta USB contenente il file immagine.<br>Il file immagine deve essere presente nella cartella principale della chiavetta USB. + + + + OptionsGeneralTab + + + Set hostname: + Imposta nome host: + + + + Set username and password + Imposta nome utente e password + + + + Username: + Nome utente: + + + + + Password: + Password: + + + + Configure wireless LAN + Configura WiFi + + + + SSID: + SSID: + + + + Show password + Visualizza password + + + + Hidden SSID + SSID nascosto + + + + Wireless LAN country: + Nazione WiFi: + + + + Set locale settings + Imposta configurazioni locali + + + + Time zone: + Fuso orario: + + + + Keyboard layout: + Layout tastiera: + + + + OptionsMiscTab + + + Play sound when finished + Riproduci suono quando completato + + + + Eject media when finished + Espelli media quando completato + + + + Enable telemetry + Abilita telemetria + + OptionsPopup - + OS Customization Personalizzazione SO @@ -305,131 +503,148 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos da usare sempre - + General Generale - + Services Servizi - + Options Opzioni - Set hostname: - Imposta nome host: + Imposta nome host: - Set username and password - Imposta nome utente e password + Imposta nome utente e password - Username: - Nome utente: + Nome utente: - - Password: - Password: + Password: - Configure wireless LAN - Configura WiFi + Configura WiFi - SSID: - SSID: + SSID: - Show password - Visualizza password + Visualizza password - Hidden SSID - SSID nascosto + SSID nascosto - Wireless LAN country: - Nazione WiFi: + Nazione WiFi: - Set locale settings - Imposta configurazioni locali + Imposta configurazioni locali - Time zone: - Fuso orario: + Fuso orario: - Keyboard layout: - Layout tastiera: + Layout tastiera: - Enable SSH - Abilita SSH + Abilita SSH - Use password authentication - Usa password autenticazione + Usa password autenticazione - Allow public-key authentication only - Permetti solo autenticazione con chiave pubblica + Permetti solo autenticazione con chiave pubblica - Set authorized_keys for '%1': - Imposta authorized_keys per '%1': + Imposta authorized_keys per '%1': - RUN SSH-KEYGEN - ESEGUI SSH-KEYGEN + ESEGUI SSH-KEYGEN - Play sound when finished - Riproduci suono quando completato + Riproduci suono quando completato - Eject media when finished - Espelli media quando completato + Espelli media quando completato - Enable telemetry - Abilita telemetria + Abilita telemetria - + SAVE SALVA + + OptionsServicesTab + + + Enable SSH + Abilita SSH + + + + Use password authentication + Usa password autenticazione + + + + Allow public-key authentication only + Permetti solo autenticazione con chiave pubblica + + + + Set authorized_keys for '%1': + Imposta authorized_keys per '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + ESEGUI SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Lettore scheda SD interno @@ -437,32 +652,31 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos UseSavedSettingsPopup - Use OS customization? - Vuoi usare la personalizzaizone SO? + Vuoi usare la personalizzaizone SO? - + Would you like to apply OS customization settings? Vuoi applicare le impostazioni personalizzazione sistema operativo? - + NO NO - + NO, CLEAR SETTINGS NO, AZZERA IMPOSTAZIONI - + YES SI' - + EDIT SETTINGS MODIFICA IMPOSTAZIONI @@ -470,62 +684,57 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v. %1 - - + Raspberry Pi Device Dispositivo Raspberry Pi - CHOOSE DEVICE - SCEGLI DISPOSITIVO + SCEGLI DISPOSITIVO - + Select this button to choose your target Raspberry Pi Seleziona questo pulsante per scegliere il Raspberry Pi destinazione - - + Operating System Sistema operativo - - + + CHOOSE OS SCEGLI S.O. - + Select this button to change the operating system Seleziona questo pulsante per modificare il sistema operativo scelto - - + Storage Scheda SD - + Network not ready yet Rete non ancora pronta - No storage devices found - Nessun dispositivo archiviazione trovato + Nessun dispositivo archiviazione trovato - - + + CHOOSE STORAGE SCEGLI SCHEDA SD @@ -534,40 +743,40 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos SCRIVI - + Select this button to change the destination storage device Seleziona questo pulsante per modificare il dispositivo archiviazione destinazione - + CANCEL WRITE ANNULLA SCRITTURA - - + + Cancelling... Annullamento... - + CANCEL VERIFY ANNULLA VERIFICA - - - + + + Finalizing... Finalizzazione... - + Next Avanti - + Select this button to start writing the image Seleziona questo pulsante per avviare la scrittura del file immagine @@ -576,22 +785,22 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos Seleziona questo pulsante per accedere alle impostazioni avanzate - + Using custom repository: %1 Usa repository personalizzato: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Navigazione da tastiera: <tab> vai al prossimo pulsante <spazio> premi il pulsante/seleziona la voce <freccia su/giù> vai su/giù negli elenchi - + Language: Lingua: - + Keyboard: Tastiera: @@ -600,84 +809,73 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos Modello Pi: - [ All ] - [ Tutti ] + [ Tutti ] - Back - Indietro + Indietro - Go back to main menu - Torna al menu principale + Torna al menu principale - Released: %1 - Rilasciato: %1 + Rilasciato: %1 - Cached on your computer - Memorizzato temporaneamente nel computer + Memorizzato temporaneamente nel computer - Local file - File locale + File locale - Online - %1 GB download - Online - Download %1 GB + Online - Download %1 GB - - - Mounted as %1 - Montato come %1 + Montato come %1 - [WRITE PROTECTED] - [PROTETTA DA SCRITTURA] + [PROTETTA DA SCRITTURA] - + Are you sure you want to quit? Sei sicuro di voler uscire? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Image è occupato.<br>Sei sicuro di voler uscire? - + Warning Attenzione - + Preparing to write... Preparazione scrittura... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Tutti i dati esistenti in '%1' verranno eliminati.<br>Sei sicuro di voler continuare? - + Update available Aggiornamento disponibile - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? È disponibile una nuova versione di Imager.<br>Vuoi visitare il sito web per scaricare la nuova versione? @@ -686,75 +884,72 @@ Aggiungi sia 'rpi-imager.exe' che 'fat32format.exe' all&apos Errore durante download elenco SO da internet - + Writing... %1% Scrittura...%1 - + Verifying... %1% Verifica...%1 - + Preparing to write... (%1) Preparazione scrittura... (%1) - + Error Errore - + Write Successful Scrittura completata senza errori - - + + Erase Cancella - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader Azzeramento di <b>%1</b> completato<br><br>Ora puoi rimuovere la scheda SD dal lettore - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader Scrittura di <b>%1</b> in <b>%2</b> completata<br><br>Ora puoi rimuovere la scheda SD dal lettore - Error parsing os_list.json - Errore durante analisi file os_list.json + Errore durante analisi file os_list.json - + Format card as FAT32 Formatta scheda come FAT32 - + Use custom Usa immagine personalizzata - + Select a custom .img from your computer Seleziona un file immagine .img personalizzato - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Prima collega una chiavetta USB contenente il file immagine.<br>Il file immagine deve essere presente nella cartella principale della chiavetta USB. + Prima collega una chiavetta USB contenente il file immagine.<br>Il file immagine deve essere presente nella cartella principale della chiavetta USB. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - La scheda SD è protetta da scrittura.<br>Sposta verso l'alto l'interruttore LOCK sul lato sinistro della scheda SD e riprova. + La scheda SD è protetta da scrittura.<br>Sposta verso l'alto l'interruttore LOCK sul lato sinistro della scheda SD e riprova. diff --git a/src/i18n/rpi-imager_ja.ts b/src/i18n/rpi-imager_ja.ts index f5e6ce0f..709f890b 100644 --- a/src/i18n/rpi-imager_ja.ts +++ b/src/i18n/rpi-imager_ja.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 アーカイブを展開するのに失敗しました - + Error mounting FAT32 partition FAT32パーティションをマウントできませんでした - + Operating system did not mount FAT32 partition OSがFAT32パーティションをマウントしませんでした - + Error changing to directory '%1' カレントディレクトリを%1に変更できませんでした @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive ドライブをアンマウントしています - + opening drive デバイスを開いています - + Error running diskpart: %1 diskpartの実行に失敗しました: %1 - + Error removing existing partitions 既に有るパーティションを削除する際にエラーが発生しました。 - + Authentication cancelled 認証がキャンセルされました - + Error running authopen to gain access to disk device '%1' ディスク%1にアクセスするための権限を取得するためにauthopenを実行するのに失敗しました - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Raspberry Pi Imagerがリムーバブルボリュームへアクセスすることが「プライバシーとセキュリティ」の「ファイルとフォルダー」の設定、または「フルディスクアクセス」の付与によって許可されているかを確認してください。 - + Cannot open storage device '%1'. ストレージを開けませんでした。 - + discarding existing data on drive ドライブの現存するすべてのデータを破棄します - + zeroing out first and last MB of drive ドライブの最初と最後のMBを削除しています - + Write error while zero'ing out MBR MBRを削除している際にエラーが発生しました。 - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). カードの最後のパートを0で書き込む際書き込みエラーが発生しました。カードが示している容量と実際のカードの容量が違う可能性があります。 - + starting download ダウンロードを開始中 - + Error downloading: %1 %1をダウンロードする際エラーが発生しました - + Access denied error while writing file to disk. ディスクにファイルを書き込む際にアクセスが拒否されました。 - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. フォルダーへのアクセスが制限されています。許可されたアプリにrpi-imager.exeとfat32format.exeを入れてもう一度お試しください。 - + Error writing file to disk ファイルをディスクに書き込んでいる際にエラーが発生しました - + Download corrupt. Hash does not match ダウンロードに失敗しました。ハッシュ値が一致していません。 - - + + Error writing to storage (while flushing) ストレージへの書き込み中にエラーが発生しました (フラッシング中) - - + + Error writing to storage (while fsync) ストレージへの書き込み中にエラーが発生しました(fsync中) - + Error writing first block (partition table) 最初のブロック(パーティションテーブル)を書き込み中にエラーが発生しました - + Error reading from storage.<br>SD card may be broken. ストレージを読むのに失敗しました。SDカードが壊れている可能性があります。 - + Verifying write failed. Contents of SD card is different from what was written to it. 確認中にエラーが発生しました。書き込んだはずのデータが実際にSDカードに記録されたデータと一致していません。 - + Customizing image イメージをカスタマイズしています @@ -250,40 +250,105 @@ このプラットフォームではフォーマットできません。 + + DstPopup + + + Storage + ストレージ + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + %1 としてマウントされています + + + + GB + + + + + [WRITE PROTECTED] + [書き込み禁止] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SDカードへの書き込みが制限されています。<br>カードの左上にあるロックスイッチを上げてロックを解除し、もう一度お試しください。 + + + + HWListModel + + + CHOOSE DEVICE + デバイスを選択 + + + + HwPopup + + + Raspberry Pi Device + Raspberry Piデバイス + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. ストレージの容量が足りません。少なくとも%1GBは必要です。 - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. 入力されたファイルは適切なディスクイメージファイルではありません。ファイルサイズの%1は512バイトの倍数ではありません。 - + Downloading and writing image イメージをダウンロードして書き込んでいます - + Select image イメージを選ぶ - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? Wifiのパスワードをシステムのキーチェーンから読み取って設定しますか? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file イメージファイルを開いています - + Error opening image file イメージファイルを開く際にエラーが発生しました @@ -304,30 +369,163 @@ MsgPopup - + NO いいえ - + YES はい - + CONTINUE 続ける - + QUIT やめる + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + OS + + + + Back + 戻る + + + + Go back to main menu + メインメニューへ戻る + + + + Released: %1 + リリース日時: %1 + + + + Cached on your computer + コンピュータにキャッシュされたファイル + + + + Local file + ローカルファイル + + + + Online - %1 GB download + インターネットからダウンロード - %1 GB + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + 最初にイメージファイルがあるUSBメモリを接続してください。<br>イメージファイルはUSBメモリの一番上(ルートフォルダー)に入れてください。 + + + + OptionsGeneralTab + + + Set hostname: + ホスト名: + + + + Set username and password + ユーザー名とパスワードを設定する + + + + Username: + ユーザー名 + + + + + Password: + パスワード: + + + + Configure wireless LAN + Wi-Fiを設定する + + + + SSID: + SSID: + + + + Show password + パスワードを見る + + + + Hidden SSID + ステルスSSID + + + + Wireless LAN country: + Wifiを使う国: + + + + Set locale settings + ロケール設定をする + + + + Time zone: + タイムゾーン: + + + + Keyboard layout: + キーボードレイアウト: + + + + OptionsMiscTab + + + Play sound when finished + 終わったときに音を鳴らす + + + + Eject media when finished + 終わったときにメディアを取り出す + + + + Enable telemetry + テレメトリーを有効化 + + OptionsPopup - + OS Customization @@ -340,123 +538,102 @@ いつも使う設定にする - + General 一般 - + Services サービス - + Options オプション - Set hostname: - ホスト名: + ホスト名: - Set username and password - ユーザー名とパスワードを設定する + ユーザー名とパスワードを設定する - Username: - ユーザー名 + ユーザー名 - - Password: - パスワード: + パスワード: - Configure wireless LAN - Wi-Fiを設定する + Wi-Fiを設定する - SSID: - SSID: + SSID: - Show password - パスワードを見る + パスワードを見る - Hidden SSID - ステルスSSID + ステルスSSID - Wireless LAN country: - Wifiを使う国: + Wifiを使う国: - Set locale settings - ロケール設定をする + ロケール設定をする - Time zone: - タイムゾーン: + タイムゾーン: - Keyboard layout: - キーボードレイアウト: + キーボードレイアウト: - Enable SSH - SSHを有効化する + SSHを有効化する - Use password authentication - パスワード認証を使う + パスワード認証を使う - Allow public-key authentication only - 公開鍵認証のみを許可する + 公開鍵認証のみを許可する - Set authorized_keys for '%1': - ユーザー%1のためのauthorized_keys + ユーザー%1のためのauthorized_keys - RUN SSH-KEYGEN - ssh-keygenを実行する + ssh-keygenを実行する - Play sound when finished - 終わったときに音を鳴らす + 終わったときに音を鳴らす - Eject media when finished - 終わったときにメディアを取り出す + 終わったときにメディアを取り出す - Enable telemetry - テレメトリーを有効化 + テレメトリーを有効化 - + SAVE 保存 @@ -473,10 +650,48 @@ 永続的な設定 + + OptionsServicesTab + + + Enable SSH + SSHを有効化する + + + + Use password authentication + パスワード認証を使う + + + + Allow public-key authentication only + 公開鍵認証のみを許可する + + + + Set authorized_keys for '%1': + ユーザー%1のためのauthorized_keys + + + + Delete Key + + + + + RUN SSH-KEYGEN + ssh-keygenを実行する + + + + Add SSH Key + + + QObject - + Internal SD card reader SDカードリーダー @@ -484,32 +699,27 @@ UseSavedSettingsPopup - - Use OS customization? - - - - + Would you like to apply OS customization settings? - + NO いいえ - + NO, CLEAR SETTINGS いいえ、設定をクリアする - + YES はい - + EDIT SETTINGS 設定を編集する @@ -517,62 +727,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Raspberry Piデバイス - CHOOSE DEVICE - デバイスを選択 + デバイスを選択 - + Select this button to choose your target Raspberry Pi 対象のRaspberry Piを選択するには、このボタンを押してください。 - - + Operating System OS - - + + CHOOSE OS OSを選択 - + Select this button to change the operating system OSを変更するにはこのボタンを押してください - - + Storage ストレージ - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE ストレージを選択 @@ -581,40 +782,40 @@ 書き込む - + Select this button to change the destination storage device 書き込み先のストレージデバイスを選択するにはこのボタンを押してください - + CANCEL WRITE 書き込みをキャンセル - - + + Cancelling... キャンセル中です... - + CANCEL VERIFY 確認をやめる - - - + + + Finalizing... 最終処理をしています... - + Next 次へ - + Select this button to start writing the image 書き込みをスタートさせるにはこのボタンを押してください @@ -623,22 +824,22 @@ 詳細な設定を変更するのならこのボタンを押してください - + Using custom repository: %1 カスタムレポジトリを使います: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists キーボードの操作: 次のボタンに移動する→Tabキー ボタンを押す/選択する→Spaceキー 上に行く/下に行く→矢印キー(上下) - + Language: 言語: - + Keyboard: キーボード: @@ -647,84 +848,73 @@ Raspberry Pi モデル: - [ All ] - [すべて] + [すべて] - Back - 戻る + 戻る - Go back to main menu - メインメニューへ戻る + メインメニューへ戻る - Released: %1 - リリース日時: %1 + リリース日時: %1 - Cached on your computer - コンピュータにキャッシュされたファイル + コンピュータにキャッシュされたファイル - Local file - ローカルファイル + ローカルファイル - Online - %1 GB download - インターネットからダウンロード - %1 GB + インターネットからダウンロード - %1 GB - - - Mounted as %1 - %1 としてマウントされています + %1 としてマウントされています - [WRITE PROTECTED] - [書き込み禁止] + [書き込み禁止] - + Are you sure you want to quit? 本当にやめますか? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imagerは現在まだ処理中です。<bt>本当にやめますか? - + Warning 警告 - + Preparing to write... 書き込み準備中... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? %1 に存在するすべてのデータは完全に削除されます。本当に続けますか? - + Update available アップデートがあります - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? 新しいバージョンのImagerがあります。<br>ダウンロードするためにウェブサイトを開きますか? @@ -733,75 +923,72 @@ OSのリストをダウンロードする際にエラーが発生しました。 - + Writing... %1% 書き込み中... %1% - + Verifying... %1% 確認中... %1% - + Preparing to write... (%1) 書き込み準備中... (%1) - + Error エラー - + Write Successful 書き込みが正常に終了しました - - + + Erase 削除 - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b%gt;%1</b> は削除されました。<br><bt>SDカードをSDカードリーダーから取り出すことができます。 - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> は<b>%2</b>に書き込まれました。<br><br>SDカードをSDカードリーダーから取り出すことができます。 - Error parsing os_list.json - os_list.jsonの処理中にエラーが発生しました + os_list.jsonの処理中にエラーが発生しました - + Format card as FAT32 カードをFAT32でフォーマットする - + Use custom カスタムイメージを使う - + Select a custom .img from your computer 自分で用意したイメージファイルを使う - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - 最初にイメージファイルがあるUSBメモリを接続してください。<br>イメージファイルはUSBメモリの一番上(ルートフォルダー)に入れてください。 + 最初にイメージファイルがあるUSBメモリを接続してください。<br>イメージファイルはUSBメモリの一番上(ルートフォルダー)に入れてください。 - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SDカードへの書き込みが制限されています。<br>カードの左上にあるロックスイッチを上げてロックを解除し、もう一度お試しください。 + SDカードへの書き込みが制限されています。<br>カードの左上にあるロックスイッチを上げてロックを解除し、もう一度お試しください。 <b>%1</b> has been written to <b>%2</b> diff --git a/src/i18n/rpi-imager_ko.ts b/src/i18n/rpi-imager_ko.ts index 40d4ed7d..d78a3a60 100644 --- a/src/i18n/rpi-imager_ko.ts +++ b/src/i18n/rpi-imager_ko.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 보관 파일 추출 오류: %1 - + Error mounting FAT32 partition FAT32 파티션 마운트 오류 - + Operating system did not mount FAT32 partition 운영 체제에서 FAT32 파티션을 마운트하지 않음 - + Error changing to directory '%1' 디렉토리 변경 오류 '%1' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive 드라이브 마운트 해제 - + opening drive 드라이브 열기 - + Error running diskpart: %1 디스크 파트를 실행하는 동안 오류 발생 : %1 - + Error removing existing partitions 기존 파티션 제거 오류 - + Authentication cancelled 인증 취소됨 - + Error running authopen to gain access to disk device '%1' 디스크 디바이스에 대한 액세스 권한을 얻기 위해 authopen을 실행하는 중 오류 발생 '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). 'Raspberry Pi Imager'가 개인 정보 설정('파일 및 폴더'에서 또는 '전체 디스크 액세스'를 부여)에서 '제거 가능한 볼륨'에 액세스할 수 있는지 확인하세요. - + Cannot open storage device '%1'. 저장 장치를 열 수 없습니다 '%1'. - + discarding existing data on drive 드라이브의 기존 데이터 삭제 - + zeroing out first and last MB of drive 드라이브의 처음과 마지막 MB를 비웁니다. - + Write error while zero'ing out MBR MBR을 zero'ing out 하는 동안 쓰기 오류 발생 - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). SD card의 마지막 부분을 비워내는 동안 오류가 발생했습니다.<br>카드가 잘못된 용량을 표기하고 있을 수 있습니다(위조 품목). - + starting download 다운로드 시작 - + Error downloading: %1 다운로드 중 오류: %1 - + Access denied error while writing file to disk. 디스크에 파일을 쓰는 동안 액세스 거부 오류가 발생했습니다. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. 제어된 폴더 액세스가 설정된 것 같습니다. rpi-imager.exe 및 fat32format.exe를 허용된 앱 리스트에서 추가하고 다시 시도하십시오. - + Error writing file to disk 디스크에 파일 쓰기 오류 - + Download corrupt. Hash does not match 다운로드가 손상되었습니다. 해시가 일치하지 않습니다. - - + + Error writing to storage (while flushing) 저장소에 쓰는 중 오류 발생(flushing..) - - + + Error writing to storage (while fsync) 스토리지에 쓰는 중 오류 발생(fsync..) - + Error writing first block (partition table) 첫 번째 블록을 쓰는 중 오류 발생 (파티션 테이블) - + Error reading from storage.<br>SD card may be broken. 저장소에서 읽는 동안 오류가 발생했습니다.<br>SD 카드가 고장 났을 수 있습니다. - + Verifying write failed. Contents of SD card is different from what was written to it. 쓰기를 확인하지 못했습니다. SD카드 내용과 쓰인 내용이 다릅니다. - + Customizing image 이미지 사용자 정의 @@ -250,40 +250,105 @@ 이 플랫폼에 대해 포맷이 구현되지 않았습니다. + + DstPopup + + + Storage + 저장소 + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + %1로 마운트 + + + + GB + + + + + [WRITE PROTECTED] + [쓰기 보호됨] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + 쓰기 금지된 SD card<br>카드 왼쪽에 있는 잠금 스위치를 위쪽으로 누른 후 다시 시도하십시오. + + + + HWListModel + + + CHOOSE DEVICE + 장치 선택 + + + + HwPopup + + + Raspberry Pi Device + 라즈베리파이(Raspberry Pi) 디바이스 + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. 저장소 공간이 충분하지 않습니다.<br>최소 %1 GB 이상이어야 합니다. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. 입력 파일이 올바른 디스크 이미지가 아닙니다.<br>파일사이즈 %1 바이트가 512바이트의 배수가 아닙니다. - + Downloading and writing image 이미지 다운로드 및 쓰기 - + Select image 이미지 선택하기 - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? wifi password를 미리 입력하시겠습니까? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file 이미지 파일 열기 - + Error opening image file 이미지 파일 열기 오류 @@ -304,30 +369,163 @@ MsgPopup - + NO 아니요 - + YES - + CONTINUE 계속 - + QUIT 나가기 + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + 운영체제 + + + + Back + 뒤로 가기 + + + + Go back to main menu + 기본 메뉴로 돌아가기 + + + + Released: %1 + 릴리즈: %1 + + + + Cached on your computer + 컴퓨터에 캐시됨 + + + + Local file + 로컬 파일 + + + + Online - %1 GB download + 온라인 - %1 GB 다운로드 + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + 먼저 이미지가 들어 있는 USB를 연결합니다.<br>이미지는 USB 루트 폴더에 있어야 합니다. + + + + OptionsGeneralTab + + + Set hostname: + hostname 설정: + + + + Set username and password + 사용자 이름 및 비밀번호 설정 + + + + Username: + 사용자 이름: + + + + + Password: + 비밀번호: + + + + Configure wireless LAN + 무선 LAN 설정 + + + + SSID: + SSID: + + + + Show password + 비밀번호 표시 + + + + Hidden SSID + 숨겨진 SSID + + + + Wireless LAN country: + 무선 LAN 국가: + + + + Set locale settings + 로케일 설정 지정 + + + + Time zone: + 시간대: + + + + Keyboard layout: + 키보드 레이아웃: + + + + OptionsMiscTab + + + Play sound when finished + 완료되면 효과음으로 알림 + + + + Eject media when finished + 완료되면 미디어 꺼내기 + + + + Enable telemetry + 이미지 다운로드 통계 관하여 정보 수집 허용 + + OptionsPopup - + OS Customization OS 커스터마이징 @@ -344,123 +542,102 @@ 항상 사용 - + General 일반 - + Services 서비스 - + Options 옵션 - Set hostname: - hostname 설정: + hostname 설정: - Set username and password - 사용자 이름 및 비밀번호 설정 + 사용자 이름 및 비밀번호 설정 - Username: - 사용자 이름: + 사용자 이름: - - Password: - 비밀번호: + 비밀번호: - Configure wireless LAN - 무선 LAN 설정 + 무선 LAN 설정 - SSID: - SSID: + SSID: - Show password - 비밀번호 표시 + 비밀번호 표시 - Hidden SSID - 숨겨진 SSID + 숨겨진 SSID - Wireless LAN country: - 무선 LAN 국가: + 무선 LAN 국가: - Set locale settings - 로케일 설정 지정 + 로케일 설정 지정 - Time zone: - 시간대: + 시간대: - Keyboard layout: - 키보드 레이아웃: + 키보드 레이아웃: - Enable SSH - SSH 사용 + SSH 사용 - Use password authentication - 비밀번호 인증 사용 + 비밀번호 인증 사용 - Allow public-key authentication only - 공개 키만 인증 허용 + 공개 키만 인증 허용 - Set authorized_keys for '%1': - '%1' 인증키 설정: + '%1' 인증키 설정: - RUN SSH-KEYGEN - SSH-KEYGEN 실행 + SSH-KEYGEN 실행 - Play sound when finished - 완료되면 효과음으로 알림 + 완료되면 효과음으로 알림 - Eject media when finished - 완료되면 미디어 꺼내기 + 완료되면 미디어 꺼내기 - Enable telemetry - 이미지 다운로드 통계 관하여 정보 수집 허용 + 이미지 다운로드 통계 관하여 정보 수집 허용 - + SAVE 저장 @@ -477,10 +654,48 @@ 영구적으로 설정 + + OptionsServicesTab + + + Enable SSH + SSH 사용 + + + + Use password authentication + 비밀번호 인증 사용 + + + + Allow public-key authentication only + 공개 키만 인증 허용 + + + + Set authorized_keys for '%1': + '%1' 인증키 설정: + + + + Delete Key + + + + + RUN SSH-KEYGEN + SSH-KEYGEN 실행 + + + + Add SSH Key + + + QObject - + Internal SD card reader 내장 SD 카드 리더기 @@ -488,32 +703,31 @@ UseSavedSettingsPopup - Use OS customization? - OS 커스터마이징을 사용하십니까? + OS 커스터마이징을 사용하십니까? - + Would you like to apply OS customization settings? OS 커스터마이징 설정을 적용하시겠습니까? - + NO 아니요 - + NO, CLEAR SETTINGS 아니요, 설정 지우기 - + YES - + EDIT SETTINGS 설정을 편집하기 @@ -521,62 +735,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device 라즈베리파이(Raspberry Pi) 디바이스 - CHOOSE DEVICE - 장치 선택 + 장치 선택 - + Select this button to choose your target Raspberry Pi 이 버튼을 선택하면 타켓 Raspberry Pi 를 선택할 수 있습니다 - - + Operating System 운영체제 - - + + CHOOSE OS 운영체제 선택 - + Select this button to change the operating system 운영 체제를 변경하려면 이 버튼을 선택합니다. - - + Storage 저장소 - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE 저장소 선택 @@ -585,40 +790,40 @@ 쓰기 - + Select this button to change the destination storage device 저장 디바이스를 변경하려면 버튼을 선택합니다. - + CANCEL WRITE 쓰기 취소 - - + + Cancelling... 취소 하는 중... - + CANCEL VERIFY 확인 취소 - - - + + + Finalizing... 마무리 중... - + Next 다음 - + Select this button to start writing the image 이미지 쓰기를 시작하려면 버튼을 선택합니다. @@ -627,22 +832,22 @@ 고급 설정에 액세스하려면 버튼을 선택합니다. - + Using custom repository: %1 사용자 지정 리포지토리 사용: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists 키보드 탐색: <tab> 다음 버튼으로 이동 <space> 버튼 선택 및 항목 선택 <arrow up/down> 목록에서 위아래로 이동 - + Language: 언어: - + Keyboard: 키보드: @@ -651,84 +856,73 @@ 파이 모델 - [ All ] - [ 전체 ] + [ 전체 ] - Back - 뒤로 가기 + 뒤로 가기 - Go back to main menu - 기본 메뉴로 돌아가기 + 기본 메뉴로 돌아가기 - Released: %1 - 릴리즈: %1 + 릴리즈: %1 - Cached on your computer - 컴퓨터에 캐시됨 + 컴퓨터에 캐시됨 - Local file - 로컬 파일 + 로컬 파일 - Online - %1 GB download - 온라인 - %1 GB 다운로드 + 온라인 - %1 GB 다운로드 - - - Mounted as %1 - %1로 마운트 + %1로 마운트 - [WRITE PROTECTED] - [쓰기 보호됨] + [쓰기 보호됨] - + Are you sure you want to quit? 정말 그만두시겠습니까? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager가 사용 중입니다.<br>정말 그만두시겠습니까? - + Warning 주의 - + Preparing to write... 쓰기 준비 중... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? '%1'에 존재하는 모든 데이터가 지워집니다.<br>계속하시겠습니까? - + Update available 업데이트 가능 - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Raspberry Pi Imager의 최신 버전을 사용할 수 있습니다.<br>다운받기 위해 웹사이트를 방문하시겠습니까?? @@ -737,75 +931,72 @@ 인터넷에서 OS 목록을 다운로드하는 중 오류 발생 - + Writing... %1% 쓰는 중... %1% - + Verifying... %1% 확인 중... %1% - + Preparing to write... (%1) 쓰기 준비 중... (%1) - + Error 오류 - + Write Successful 쓰기 완료 - - + + Erase 삭제 - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> 가 지워졌습니다.<br><br>이제 SD card를 제거할 수 있습니다. - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b>가 <b>%2</b><br><br>에 쓰여졌습니다. 이제 SD card를 제거할 수 있습니다. - Error parsing os_list.json - 구문 분석 오류 os_list.json + 구문 분석 오류 os_list.json - + Format card as FAT32 FAT32로 카드 형식 지정 - + Use custom 사용자 정의 사용 - + Select a custom .img from your computer 컴퓨터에서 사용자 지정 .img를 선택합니다. - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - 먼저 이미지가 들어 있는 USB를 연결합니다.<br>이미지는 USB 루트 폴더에 있어야 합니다. + 먼저 이미지가 들어 있는 USB를 연결합니다.<br>이미지는 USB 루트 폴더에 있어야 합니다. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - 쓰기 금지된 SD card<br>카드 왼쪽에 있는 잠금 스위치를 위쪽으로 누른 후 다시 시도하십시오. + 쓰기 금지된 SD card<br>카드 왼쪽에 있는 잠금 스위치를 위쪽으로 누른 후 다시 시도하십시오. <b>%1</b> has been written to <b>%2</b> diff --git a/src/i18n/rpi-imager_nl.ts b/src/i18n/rpi-imager_nl.ts index b5ed75bf..8c6052e8 100644 --- a/src/i18n/rpi-imager_nl.ts +++ b/src/i18n/rpi-imager_nl.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Fout bij uitpakken archiefbestand: %1 - + Error mounting FAT32 partition Fout bij mounten FAT32 partitie - + Operating system did not mount FAT32 partition Besturingssysteem heeft FAT32 partitie niet gemount - + Error changing to directory '%1' Fout bij openen map '%1' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive unmounten van schijf - + opening drive openen van opslag - + Error running diskpart: %1 Fout bij uitvoeren diskpart: %1 - + Error removing existing partitions Fout bij verwijderen bestaande partities - + Authentication cancelled Authenticatie geannuleerd - + Error running authopen to gain access to disk device '%1' Fout bij uitvoeren authopen: '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Gelieve te controlleren of 'Raspberry Pi Imager' toegang heeft tot 'verwijderbare volumes' in de privacy instellingen (onder 'bestanden en mappen' of anders via 'volledige schijftoegang'). - + Cannot open storage device '%1'. Fout bij openen opslagapparaat '%1'. - + discarding existing data on drive wissen bestaande gegevens - + zeroing out first and last MB of drive wissen eerste en laatste MB van opslag - + Write error while zero'ing out MBR Fout bij wissen MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Fout bij wissen laatste deel van de SD kaart.<br>Kaart geeft mogelijk onjuiste capaciteit aan (mogelijk counterfeit). - + starting download beginnen met downloaden - + Error downloading: %1 Fout bij downloaden: %1 - + Access denied error while writing file to disk. Toegang geweigerd bij het schrijven naar opslag. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Controller Folder Access lijkt aan te staan. Gelieve zowel rpi-imager.exe als fat32format.exe toe te voegen aan de lijst met uitsluitingen en het nogmaals te proberen. - + Error writing file to disk Fout bij schrijven naar opslag - + Download corrupt. Hash does not match Download corrupt. Hash komt niet overeen - - + + Error writing to storage (while flushing) Fout bij schrijven naar opslag (tijdens flushen) - - + + Error writing to storage (while fsync) Fout bij schrijven naar opslag (tijdens fsync) - + Error writing first block (partition table) Fout bij schrijven naar eerste deel van kaart (partitie tabel) - + Error reading from storage.<br>SD card may be broken. Fout bij lezen van SD kaart.<br>Kaart is mogelijk defect. - + Verifying write failed. Contents of SD card is different from what was written to it. Verificatie mislukt. De gegevens die op de SD kaart staan wijken af van wat er naar geschreven is. - + Customizing image Bezig met aanpassen besturingssysteem @@ -250,40 +250,105 @@ Formatteren is niet geimplementeerd op dit besturingssysteem + + DstPopup + + + Storage + Opslagapparaat + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Mounted op %1 + + + + GB + + + + + [WRITE PROTECTED] + [ALLEEN LEZEN] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD kaart is tegen schrijven beveiligd.<br>Druk het schuifje aan de linkerkant van de SD kaart omhoog, en probeer nogmaals. + + + + HWListModel + + + CHOOSE DEVICE + KIES MODEL + + + + HwPopup + + + Raspberry Pi Device + Raspberry Pi Model + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Opslagcapaciteit niet groot genoeg.<br>Deze dient minimaal %1 GB te zijn. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Invoerbestand is geen disk image.<br>Bestandsgrootte %1 bytes is geen veelvoud van 512 bytes. - + Downloading and writing image Downloaden en schrijven van image - + Select image Selecteer image - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? Wilt u het wifi wachtwoord van het systeem overnemen? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file openen image - + Error opening image file Fout bij openen image bestand @@ -304,30 +369,163 @@ MsgPopup - + NO Nee - + YES Ja - + CONTINUE VERDER GAAN - + QUIT AFSLUITEN + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Besturingssysteem + + + + Back + Terug + + + + Go back to main menu + Terug naar hoofdmenu + + + + Released: %1 + Release datum: %1 + + + + Cached on your computer + Opgeslagen op computer + + + + Local file + Lokaal bestand + + + + Online - %1 GB download + Online %1 GB download + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Sluit eerst een USB stick met images aan.<br>De images moeten in de hoofdmap van de USB stick staan. + + + + OptionsGeneralTab + + + Set hostname: + Hostnaam: + + + + Set username and password + Gebruikersnaam en wachtwoord instellen + + + + Username: + Gebruikersnaam: + + + + + Password: + Wachtwoord: + + + + Configure wireless LAN + Wifi instellen + + + + SSID: + SSID: + + + + Show password + Wachtwoord laten zien + + + + Hidden SSID + Verborgen SSID + + + + Wireless LAN country: + Wifi land: + + + + Set locale settings + Regio instellingen + + + + Time zone: + Tijdzone: + + + + Keyboard layout: + Toetsenbord indeling: + + + + OptionsMiscTab + + + Play sound when finished + Geluid afspelen zodra voltooid + + + + Eject media when finished + Media uitwerpen zodra voltooid + + + + Enable telemetry + Telemetry inschakelen + + OptionsPopup - + OS Customization OS aanpassen @@ -344,123 +542,102 @@ om altijd te gebruiken - + General Algemeen - + Services Services - + Options Opties - Set hostname: - Hostnaam: + Hostnaam: - Set username and password - Gebruikersnaam en wachtwoord instellen + Gebruikersnaam en wachtwoord instellen - Username: - Gebruikersnaam: + Gebruikersnaam: - - Password: - Wachtwoord: + Wachtwoord: - Configure wireless LAN - Wifi instellen + Wifi instellen - SSID: - SSID: + SSID: - Show password - Wachtwoord laten zien + Wachtwoord laten zien - Hidden SSID - Verborgen SSID + Verborgen SSID - Wireless LAN country: - Wifi land: + Wifi land: - Set locale settings - Regio instellingen + Regio instellingen - Time zone: - Tijdzone: + Tijdzone: - Keyboard layout: - Toetsenbord indeling: + Toetsenbord indeling: - Enable SSH - SSH inschakelen + SSH inschakelen - Use password authentication - Gebruik wachtwoord authenticatie + Gebruik wachtwoord authenticatie - Allow public-key authentication only - Gebruik uitsluitend public-key authenticatie + Gebruik uitsluitend public-key authenticatie - Set authorized_keys for '%1': - authorized_keys voor '%1': + authorized_keys voor '%1': - RUN SSH-KEYGEN - START SSH-KEYGEN + START SSH-KEYGEN - Play sound when finished - Geluid afspelen zodra voltooid + Geluid afspelen zodra voltooid - Eject media when finished - Media uitwerpen zodra voltooid + Media uitwerpen zodra voltooid - Enable telemetry - Telemetry inschakelen + Telemetry inschakelen - + SAVE OPSLAAN @@ -485,10 +662,48 @@ Permanente instellingen + + OptionsServicesTab + + + Enable SSH + SSH inschakelen + + + + Use password authentication + Gebruik wachtwoord authenticatie + + + + Allow public-key authentication only + Gebruik uitsluitend public-key authenticatie + + + + Set authorized_keys for '%1': + authorized_keys voor '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + START SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Interne SD kaart lezer @@ -496,32 +711,31 @@ UseSavedSettingsPopup - Use OS customization? - OS aanpassen? + OS aanpassen? - + Would you like to apply OS customization settings? Wilt u uw eigen instellingen op het OS toepassen? - + NO Nee - + NO, CLEAR SETTINGS Nee, wis instellingen - + YES Ja - + EDIT SETTINGS Aanpassen @@ -529,62 +743,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Raspberry Pi Model - CHOOSE DEVICE - KIES MODEL + KIES MODEL - + Select this button to choose your target Raspberry Pi Kies deze knop om het Raspberry Pi model te selecteren - - + Operating System Besturingssysteem - - + + CHOOSE OS KIES OS - + Select this button to change the operating system Kies deze knop om een besturingssysteem te kiezen - - + Storage Opslagapparaat - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE KIES OPSLAG @@ -593,40 +798,40 @@ SCHRIJF - + Select this button to change the destination storage device Klik op deze knop om het opslagapparaat te wijzigen - + CANCEL WRITE Annuleer schrijven - - + + Cancelling... Annuleren... - + CANCEL VERIFY ANNULEER VERIFICATIE - - - + + + Finalizing... Afronden... - + Next Volgende - + Select this button to start writing the image Kies deze knop om te beginnen met het schrijven van de image @@ -635,22 +840,22 @@ Klik op deze knop om de geadvanceerde instellingen te openen - + Using custom repository: %1 Custom repository in gebruik: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Toetsenbord navigatie: <tab> ga naar volgende knop <spatie> druk op knop/selecteer item <pijltje omhoog/omlaag> ga omhoog/omlaag in lijsten - + Language: Taal: - + Keyboard: Toetsenbord: @@ -659,84 +864,73 @@ Pi model: - [ All ] - [ Alle modellen ] + [ Alle modellen ] - Back - Terug + Terug - Go back to main menu - Terug naar hoofdmenu + Terug naar hoofdmenu - Released: %1 - Release datum: %1 + Release datum: %1 - Cached on your computer - Opgeslagen op computer + Opgeslagen op computer - Local file - Lokaal bestand + Lokaal bestand - Online - %1 GB download - Online %1 GB download + Online %1 GB download - - - Mounted as %1 - Mounted op %1 + Mounted op %1 - [WRITE PROTECTED] - [ALLEEN LEZEN] + [ALLEEN LEZEN] - + Are you sure you want to quit? Weet u zeker dat u wilt afsluiten? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager is nog niet klaar.<br>Weet u zeker dat u wilt afsluiten? - + Warning Waarschuwing - + Preparing to write... Voorbereiden... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Alle bestaande gegevens op '%1' zullen verwijderd worden.<br>Weet u zeker dat u door wilt gaan? - + Update available Update beschikbaar - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Er is een nieuwere versie van Imager beschikbaar.<br>Wilt u de website bezoeken om deze te downloaden? @@ -745,75 +939,72 @@ Fout bij downloaden van lijst met besturingssystemen - + Writing... %1% Schrijven... %1% - + Verifying... %1% Verifiëren... %1% - + Preparing to write... (%1) Voorbereiden... (%1) - + Error Fout - + Write Successful Klaar met schrijven - - + + Erase Wissen - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> is gewist<br><br>U kunt nu de SD kaart uit de lezer halen - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> is geschreven naar <b>%2</b><br><br>U kunt nu de SD kaart uit de lezer halen - Error parsing os_list.json - Fout bij parsen os_list.json + Fout bij parsen os_list.json - + Format card as FAT32 Formatteer kaart als FAT32 - + Use custom Gebruik eigen bestand - + Select a custom .img from your computer Selecteer een eigen .img bestand - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Sluit eerst een USB stick met images aan.<br>De images moeten in de hoofdmap van de USB stick staan. + Sluit eerst een USB stick met images aan.<br>De images moeten in de hoofdmap van de USB stick staan. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD kaart is tegen schrijven beveiligd.<br>Druk het schuifje aan de linkerkant van de SD kaart omhoog, en probeer nogmaals. + SD kaart is tegen schrijven beveiligd.<br>Druk het schuifje aan de linkerkant van de SD kaart omhoog, en probeer nogmaals. Select this button to change the destination SD card diff --git a/src/i18n/rpi-imager_pl.ts b/src/i18n/rpi-imager_pl.ts index 94b1871e..581e86d2 100644 --- a/src/i18n/rpi-imager_pl.ts +++ b/src/i18n/rpi-imager_pl.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Błąd podczas wypakowywania archiwum: %1 - + Error mounting FAT32 partition Błąd montowania partycji FAT32 - + Operating system did not mount FAT32 partition System operacyjny nie zamontował partycji FAT32 - + Error changing to directory '%1' Błąd przejścia do katalogu "%1" @@ -32,125 +32,125 @@ DownloadThread - + unmounting drive odmontowywanie dysku - + opening drive otwieranie dysku - + Error running diskpart: %1 Błąd uruchomienia diskpart: %1 - + Error removing existing partitions Błąd usuwania istniejących partycji - + Authentication cancelled Uwierzytelnianie anulowane - + Error running authopen to gain access to disk device '%1' Błąd uruchomienia authopen w celu uzyskania dostępu do urządzenia dyskowego '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Not sure if current macOS has that option (or if it got moved/renamed) Sprawdź, czy 'Raspberry Pi Imager' ma dostęp do 'woluminów wymiennych' w ustawieniach prywatności (w 'plikach i folderach' lub alternatywnie daj mu 'pełny dostęp do dysku'). - + Cannot open storage device '%1'. Nie można otworzyć urządzenia pamięci masowej '%1'. - + discarding existing data on drive usuwanie istniejących danych na dysku - + zeroing out first and last MB of drive zerowanie pierwszego oraz ostatniego megabajta dysku - + Write error while zero'ing out MBR Błąd zapisu podczas zerowania MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Błąd zapisu podczas próby wyzerowania ostatniej części karty.<br>;Karta może pokazywać nieprawidłową pojemność (możliwe podróbki). - + starting download rozpoczęcie pobierania - + Error downloading: %1 Błąd pobierania: %1 - + Access denied error while writing file to disk. Odmowa dostępu podczas próby zapisu pliku na dysk. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Dostęp do "Folderów Kontrolowanych" wydaje się być włączony. Dodaj rpi-imager.exe i fat32format.exe do listy dozwolonych aplikacji i spróbuj ponownie. - + Error writing file to disk Błąd zapisu pliku na dysk - + Download corrupt. Hash does not match Pobrany plik jest uszkodzony. Nie zgadza się hash - - + + Error writing to storage (while flushing) Błąd zapisu podczas wykonywania: flushing - - + + Error writing to storage (while fsync) Błąd zapisu podczas wykonywania: fsync - + Error writing first block (partition table) Błąd zapisu pierwszego bloku (tablica partycji) - + Error reading from storage.<br>SD card may be broken. Błąd odczytu z pamięci masowej.<br>Karta SD może być uszkodzona - + Verifying write failed. Contents of SD card is different from what was written to it. Weryfikacja zapisu nie powiodła się. Zawartość karty SD różni się od tego, co zostało na niej zapisane. - + Customizing image Dostosowywanie obrazu @@ -251,40 +251,105 @@ Formatowanie nie zostało zaimplementowane dla tej platformy + + DstPopup + + + Storage + Dysk + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Zamontowany jako %1 + + + + GB + + + + + [WRITE PROTECTED] + [ZABEZPIECZONY PRZED ZAPISEM] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + arta SD jest zabezpieczona przed zapisem.<br>Przesuń przełącznik blokady po lewej stronie karty do góry i spróbuj ponownie. + + + + HWListModel + + + CHOOSE DEVICE + WYBIERZ MODEL + + + + HwPopup + + + Raspberry Pi Device + Model Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Pojemność pamięci nie jest wystarczająco duża.<br>Musi wynosić co najmniej %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Plik wejściowy nie jest prawidłowym obrazem dysku.<br>Rozmiar pliku %1 bajtów nie jest wielokrotnością 512 bajtów. - + Downloading and writing image Pobieranie i zapisywanie obrazu - + Select image Wybierz obraz - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? Czy chcesz wstępnie wypełnić hasło Wi-Fi z pęku kluczy systemu? @@ -292,12 +357,12 @@ LocalFileExtractThread - + opening image file otwieranie pliku obrazu - + Error opening image file Błąd podczas otwierania pliku obrazu @@ -305,30 +370,163 @@ MsgPopup - + NO NIE - + YES TAK - + CONTINUE KONTYNUUJ - + QUIT ZAMKNIJ + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + System Operacyjny + + + + Back + Cofnij + + + + Go back to main menu + Cofnij do głównego menu + + + + Released: %1 + Wydany: %1 + + + + Cached on your computer + W pamięci cache komputera + + + + Local file + Plik lokalny + + + + Online - %1 GB download + Online - Pobrano %1 GB + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Najpierw podłącz pamięć USB zawierającą obrazy.<br>Obrazy muszą znajdować się w folderze głównym pamięci USB. + + + + OptionsGeneralTab + + + Set hostname: + ustaw hostname: + + + + Set username and password + Ustaw login i hasło + + + + Username: + Login: + + + + + Password: + Hasło: + + + + Configure wireless LAN + Skonfiguruj sieć Wi-Fi + + + + SSID: + SSID: + + + + Show password + Pokaż hasło + + + + Hidden SSID + Ukryte SSID + + + + Wireless LAN country: + Kraj Wi-Fi: + + + + Set locale settings + Ustawienia lokalizacji + + + + Time zone: + Strefa czasowa: + + + + Keyboard layout: + Układ klawiatury: + + + + OptionsMiscTab + + + Play sound when finished + Odegraj dźwięk po zakończeniu + + + + Eject media when finished + Wysuń nośnik po zakończeniu + + + + Enable telemetry + Włącz telemetrię + + OptionsPopup - + OS Customization Konfiguracja systemu @@ -345,123 +543,102 @@ do użycia na zawsze - + General Ogólne - + Services Usługi - + Options Opcje - Set hostname: - ustaw hostname: + ustaw hostname: - Set username and password - Ustaw login i hasło + Ustaw login i hasło - Username: - Login: + Login: - - Password: - Hasło: + Hasło: - Configure wireless LAN - Skonfiguruj sieć Wi-Fi + Skonfiguruj sieć Wi-Fi - SSID: - SSID: + SSID: - Show password - Pokaż hasło + Pokaż hasło - Hidden SSID - Ukryte SSID + Ukryte SSID - Wireless LAN country: - Kraj Wi-Fi: + Kraj Wi-Fi: - Set locale settings - Ustawienia lokalizacji + Ustawienia lokalizacji - Time zone: - Strefa czasowa: + Strefa czasowa: - Keyboard layout: - Układ klawiatury: + Układ klawiatury: - Enable SSH - Włącz SSH + Włącz SSH - Use password authentication - Używaj uwierzytelniania hasłem + Używaj uwierzytelniania hasłem - Allow public-key authentication only - Pozwól tylko na uwierzytelnianie kluczem publiczym + Pozwól tylko na uwierzytelnianie kluczem publiczym - Set authorized_keys for '%1': - Ustaw authorized_keys dla '%1': + Ustaw authorized_keys dla '%1': - RUN SSH-KEYGEN - Uruchom SSH-KEYGEN + Uruchom SSH-KEYGEN - Play sound when finished - Odegraj dźwięk po zakończeniu + Odegraj dźwięk po zakończeniu - Eject media when finished - Wysuń nośnik po zakończeniu + Wysuń nośnik po zakończeniu - Enable telemetry - Włącz telemetrię + Włącz telemetrię - + SAVE ZAPISZ @@ -482,10 +659,48 @@ Ustawienia trwałe + + OptionsServicesTab + + + Enable SSH + Włącz SSH + + + + Use password authentication + Używaj uwierzytelniania hasłem + + + + Allow public-key authentication only + Pozwól tylko na uwierzytelnianie kluczem publiczym + + + + Set authorized_keys for '%1': + Ustaw authorized_keys dla '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + Uruchom SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Wbudowany czytnik kart SD @@ -493,32 +708,31 @@ UseSavedSettingsPopup - Use OS customization? - Zastosować ustawienia systemu operacyjnego? + Zastosować ustawienia systemu operacyjnego? - + Would you like to apply OS customization settings? Czy chcesz zastosować ustawienia personalizacji systemu operacyjnego? - + NO NIE - + NO, CLEAR SETTINGS NIE, WYCZYŚĆ USTAWIENIA - + YES TAK - + EDIT SETTINGS EDYTUJ USTAWIENIA @@ -526,62 +740,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Model Raspberry Pi - CHOOSE DEVICE - WYBIERZ MODEL + WYBIERZ MODEL - + Select this button to choose your target Raspberry Pi Wybierz ten przycisk, aby wybrać docelowy model Raspberry Pi - - + Operating System System Operacyjny - - + + CHOOSE OS WYBIERZ OS - + Select this button to change the operating system Wybierz ten przycisk, aby zmienić system operacyjny - - + Storage Dysk - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE WYBIERZ DYSK @@ -590,40 +795,40 @@ ZAPISZ - + Select this button to change the destination storage device Wybierz ten przycisk, aby zmienić docelowe urządzenie pamięci masowej - + CANCEL WRITE ANULUJ ZAPIS - - + + Cancelling... Anulowanie... - + CANCEL VERIFY ANULUJ WERYFIKACJE - - - + + + Finalizing... Finalizowanie... - + Next Kontynuuj - + Select this button to start writing the image Wybierz ten przycisk, aby rozpocząć zapisywanie obrazu @@ -632,22 +837,22 @@ Wybierz ten przycisk, aby uzyskać dostęp do ustawień zaawansowanych - + Using custom repository: %1 Używanie niestandardowego repozytorium: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Nawigacja za pomocą klawiatury: <tab> przejdź do następnego przycisku <spacja> naciśnij przycisk/wybierz element <strzałka w górę/w dół> przejdź w górę/w dół na listach - + Language: Język: - + Keyboard: Klawiatura: @@ -656,84 +861,73 @@ Model Pi: - [ All ] - [ Wszystko ] + [ Wszystko ] - Back - Cofnij + Cofnij - Go back to main menu - Cofnij do głównego menu + Cofnij do głównego menu - Released: %1 - Wydany: %1 + Wydany: %1 - Cached on your computer - W pamięci cache komputera + W pamięci cache komputera - Local file - Plik lokalny + Plik lokalny - Online - %1 GB download - Online - Pobrano %1 GB + Online - Pobrano %1 GB - - - Mounted as %1 - Zamontowany jako %1 + Zamontowany jako %1 - [WRITE PROTECTED] - [ZABEZPIECZONY PRZED ZAPISEM] + [ZABEZPIECZONY PRZED ZAPISEM] - + Are you sure you want to quit? Czy na pewno chcesz zakończyć? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager jest wciąż zajęty.<br>Czy na pewno chcesz zakończyć?? - + Warning Uwaga - + Preparing to write... Przygotowanie do zapisu... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Wszystkie istniejące dane na '%1' zostaną usunięte.<br>Czy na pewno chcesz kontynuować? - + Update available Dostępna aktualizacja - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Dostępna jest nowsza wersja Raspberry Pi Imager.<br>Czy chcesz otworzyć stronę, aby go ściągnąć? @@ -742,75 +936,72 @@ Błąd pobierania listy OS'ów z internetu - + Writing... %1% Zapisywanie... %1% - + Verifying... %1% Weryfikacja... %1% - + Preparing to write... (%1) Przygotowanie do zapisu... (%1) - + Error Błąd - + Write Successful Zapis zakończony sukcesem - - + + Erase Usuń - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> został skasowany<br><br> Możesz teraz wyjąć kartę SD z czytnika. - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> został zapisany na <b>%2</b><br><br> Możesz teraz wyjąć kartę SD z czytnika. - Error parsing os_list.json - Błąd parsowania os_list.json + Błąd parsowania os_list.json - + Format card as FAT32 Sformatuj kartę jako FAT32 - + Use custom Użyj innego obrazu - + Select a custom .img from your computer Wybierz plik .img z komputera - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Najpierw podłącz pamięć USB zawierającą obrazy.<br>Obrazy muszą znajdować się w folderze głównym pamięci USB. + Najpierw podłącz pamięć USB zawierającą obrazy.<br>Obrazy muszą znajdować się w folderze głównym pamięci USB. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - arta SD jest zabezpieczona przed zapisem.<br>Przesuń przełącznik blokady po lewej stronie karty do góry i spróbuj ponownie. + arta SD jest zabezpieczona przed zapisem.<br>Przesuń przełącznik blokady po lewej stronie karty do góry i spróbuj ponownie. Select this button to change the destination SD card diff --git a/src/i18n/rpi-imager_pt.ts b/src/i18n/rpi-imager_pt.ts index a018fc57..7cc2dfe8 100644 --- a/src/i18n/rpi-imager_pt.ts +++ b/src/i18n/rpi-imager_pt.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Erro ao extrair o arquivo: %1 - + Error mounting FAT32 partition Erro ao montar a partição FAT32 - + Operating system did not mount FAT32 partition O sistema operativo não montou a partição FAT32 - + Error changing to directory '%1' Erro ao mudar para o diretório '%1' @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive desmontando unidade - + opening drive abrindo unidade - + Error running diskpart: %1 Erro ao executar o diskpart: %1 - + Error removing existing partitions Erro ao remover partições existentes - + Authentication cancelled Autenticação cancelada - + Error running authopen to gain access to disk device '%1' Erro ao executar o authopen para obter acesso ao dispositivo de disco '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Verifique se o “Raspberry Pi Imager” tem permissão de acesso a 'volumes amovíveis' nas definições de privacidade (em 'ficheiros e pastas' ou, em alternativa, dê-lhe 'acesso total ao disco'). - + Cannot open storage device '%1'. Não foi possível abrir o dispositivo de armazenamento '%1'. - + discarding existing data on drive descartar dados existentes na unidade - + zeroing out first and last MB of drive zerar o primeiro e o último MB da unidade - + Write error while zero'ing out MBR Erro de gravação ao zerar o MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Erro de gravação ao tentar zerar a última parte do cartão.<br>O cartão pode estar a anunciar uma capacidade errada (possível contrafação). - + starting download iniciar a transferência - + Error downloading: %1 Erro ao transferir: %1 - + Access denied error while writing file to disk. Erro de acesso negado durante a gravação do ficheiro no disco. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. O Acesso Controlado a Pastas parece estar ativado. Adicione o rpi-imager.exe e o fat32format.exe à lista de aplicações permitidas e tente novamente. - + Error writing file to disk Erro ao gravar o ficheiro no disco - + Download corrupt. Hash does not match Transferência corrupta. O hash não coincide - - + + Error writing to storage (while flushing) Erro ao gravar na memória (durante a limpeza) - - + + Error writing to storage (while fsync) Erro ao gravar no armazenamento (durante fsync) - + Error writing first block (partition table) Erro ao gravar o primeiro bloco (tabela de partições) - + Error reading from storage.<br>SD card may be broken. Erro na leitura do armazenamento.<br>O cartão SD pode estar danificado. - + Verifying write failed. Contents of SD card is different from what was written to it. Erro ao verificar a gravação. O conteúdo do cartão SD é diferente do que foi gravado nele. - + Customizing image A personalizar imagem @@ -210,40 +210,105 @@ Formatação não implementada para esta plataforma + + DstPopup + + + Storage + Armazenamento + + + + No storage devices found + Não foram encontrados dispositivos de armazenamento + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Montado como %1 + + + + GB + + + + + [WRITE PROTECTED] + [PROTEGIDO CONTRA GRAVAÇÃO] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + O cartão SD está protegido contra a gravação.<br>Empurre o interrutor de bloqueio no lado esquerdo do cartão para cima e tente novamente. + + + + HWListModel + + + CHOOSE DEVICE + ESCOLHER DISPOSITIVO + + + + HwPopup + + + Raspberry Pi Device + Dispositivo Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. A capacidade de armazenamento não é suficientemente grande.<br>Precisa de ter pelo menos %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. O ficheiro de entrada não é uma imagem de disco válida.<br>O tamanho do ficheiro %1 bytes não é um múltiplo de 512 bytes. - + Downloading and writing image A transferir e a gravar imagem - + Select image Selecionar imagem - + Error synchronizing time. Trying again in 3 seconds Erro ao sincronizar a hora. Tentar novamente daqui a 3 segundos - + STP is enabled on your Ethernet switch. Getting IP will take long time. O STP está ativado no seu comutador de Ethernet. A obtenção de IP demorará muito tempo. - + Would you like to prefill the wifi password from the system keychain? Gostaria de pré-preencher a palavra-passe wifi a partir do chaveiro do sistema? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file abrindo ficheiro de imagem - + Error opening image file Erro ao abrir ficheiro de imagem @@ -264,163 +329,313 @@ MsgPopup - + NO NÃO - + YES SIM - + CONTINUE CONTINUAR - + QUIT SAIR + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Sistema operativo + + + + Back + Voltar + + + + Go back to main menu + Voltar ao menu principal + + + + Released: %1 + Lançado: %1 + + + + Cached on your computer + Em cache no seu computador + + + + Local file + Ficheiro local + + + + Online - %1 GB download + Online - transferir %1 GB + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Insira primeiro uma pen USB que contenha imagens.<br>As imagens devem estar localizadas na pasta de raiz da pen USB. + + + + OptionsGeneralTab + + + Set hostname: + Definir o nome de anfitrião: + + + + Set username and password + Definir nome de utilizador e palavra-passe + + + + Username: + Nome de utilizador: + + + + + Password: + Palavra-passe: + + + + Configure wireless LAN + Configurar a rede LAN sem fios + + + + SSID: + SSID: + + + + Show password + Mostrar palavra-passe + + + + Hidden SSID + SSID oculto + + + + Wireless LAN country: + País da rede LAN sem fios: + + + + Set locale settings + Definir definições de idioma e região + + + + Time zone: + Fuso horário: + + + + Keyboard layout: + Disposição do teclado: + + + + OptionsMiscTab + + + Play sound when finished + Reproduzir som ao terminar + + + + Eject media when finished + Ejetar suporte ao terminar + + + + Enable telemetry + Ativar telemetria + + OptionsPopup - + OS Customization Personalização do SO OS customization options - Opções de personalização do SO + Opções de personalização do SO - + General Geral - + Services Serviços - + Options Opções - Set hostname: - Definir o nome de anfitrião: + Definir o nome de anfitrião: - Set username and password - Definir nome de utilizador e palavra-passe + Definir nome de utilizador e palavra-passe - Username: - Nome de utilizador: + Nome de utilizador: - - Password: - Palavra-passe: + Palavra-passe: - Configure wireless LAN - Configurar a rede LAN sem fios + Configurar a rede LAN sem fios - SSID: - SSID: + SSID: - Show password - Mostrar palavra-passe + Mostrar palavra-passe - Hidden SSID - SSID oculto + SSID oculto - Wireless LAN country: - País da rede LAN sem fios: + País da rede LAN sem fios: - Set locale settings - Definir definições de idioma e região + Definir definições de idioma e região - Time zone: - Fuso horário: + Fuso horário: - Keyboard layout: - Disposição do teclado: + Disposição do teclado: - Enable SSH - Ativar SSH + Ativar SSH - Use password authentication - Utilizar autenticação por palavra-passe + Utilizar autenticação por palavra-passe - Allow public-key authentication only - Permitir apenas a autenticação por chave pública + Permitir apenas a autenticação por chave pública - Set authorized_keys for '%1': - Definir authorized_keys para '%1': + Definir authorized_keys para '%1': - RUN SSH-KEYGEN - EXECUTAR SSH-KEYGEN + EXECUTAR SSH-KEYGEN - Play sound when finished - Reproduzir som ao terminar + Reproduzir som ao terminar - Eject media when finished - Ejetar suporte ao terminar + Ejetar suporte ao terminar - Enable telemetry - Ativar telemetria + Ativar telemetria - + SAVE GUARDAR + + OptionsServicesTab + + + Enable SSH + Ativar SSH + + + + Use password authentication + Utilizar autenticação por palavra-passe + + + + Allow public-key authentication only + Permitir apenas a autenticação por chave pública + + + + Set authorized_keys for '%1': + Definir authorized_keys para '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + EXECUTAR SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Leitor de cartões SD interno @@ -428,32 +643,31 @@ UseSavedSettingsPopup - Use OS customization? - Utilizar a personalização do SO? + Utilizar a personalização do SO? - + Would you like to apply OS customization settings? Gostaria de aplicar definições de personalização do SO? - + NO NÃO - + NO, CLEAR SETTINGS NÃO, LIMPAR DEFINIÇÕES - + YES SIM - + EDIT SETTINGS EDITAR DEFINIÇÕES @@ -461,275 +675,256 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Dispositivo Raspberry Pi - CHOOSE DEVICE - ESCOLHER DISPOSITIVO + ESCOLHER DISPOSITIVO - + Select this button to choose your target Raspberry Pi Selecione este botão para escolher o seu Raspberry Pi de destino - - + Operating System Sistema operativo - - + + CHOOSE OS ESCOLHER SO - + Select this button to change the operating system Selecione este botão para mudar o sistema operativo - - + Storage Armazenamento - + Network not ready yet A rede ainda não está pronta - No storage devices found - Não foram encontrados dispositivos de armazenamento + Não foram encontrados dispositivos de armazenamento - - + + CHOOSE STORAGE ESCOLHER ARMAZENAMENTO - + Select this button to change the destination storage device Selecione este botão para mudar o dispositivo de armazenamento de destino - + CANCEL WRITE CANCELAR A GRAVAÇÃO - - + + Cancelling... A cancelar... - + CANCEL VERIFY CANCELAR VERIFICAÇÃO - - - + + + Finalizing... A finalizar... - + Next Seguinte - + Select this button to start writing the image Selecione este botão para começar a gravar a imagem - + Using custom repository: %1 A usar repositório personalizado: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Navegação pelo teclado: <tab> navegar para o botão seguinte <space> premir o botão/selecionar o item <seta para cima/para baixo> subir/descer nas listas - + Language: Idioma: - + Keyboard: Teclado: - [ All ] - [ Tudo ] + [ Tudo ] - Back - Voltar + Voltar - Go back to main menu - Voltar ao menu principal + Voltar ao menu principal - Released: %1 - Lançado: %1 + Lançado: %1 - Cached on your computer - Em cache no seu computador + Em cache no seu computador - Local file - Ficheiro local + Ficheiro local - Online - %1 GB download - Online - transferir %1 GB + Online - transferir %1 GB - - - Mounted as %1 - Montado como %1 + Montado como %1 - [WRITE PROTECTED] - [PROTEGIDO CONTRA GRAVAÇÃO] + [PROTEGIDO CONTRA GRAVAÇÃO] - + Are you sure you want to quit? Tem a certeza de que pretende sair? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? O Raspberry Pi Imager ainda está ocupado.<br>Tem a certeza de que pretende sair? - + Warning Aviso - + Preparing to write... A preparar para gravar... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Todos os dados existentes em '%1 serão apagados.<br>Tem a certeza de que pretende continuar? - + Update available Atualização disponível - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Está disponível uma versão mais recente do Imager.<br>Deseja visitar o website para a transferir? - + Writing... %1% A gravar... %1% - + Verifying... %1% A verificar... %1% - + Preparing to write... (%1) A preparar para gravar... (%1) - + Error Erro - + Write Successful Gravado com sucesso - - + + Erase Apagar - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> foi apagado <br><br>Pode agora remover o cartão SD do leitor - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> foi gravado para <b>%2</b><br><br>Pode agora remover o cartão SD do leitor - Error parsing os_list.json - Erro ao analisar os_list.json + Erro ao analisar os_list.json - + Format card as FAT32 Formatar cartão como FAT32 - + Use custom Utilizar personalizado - + Select a custom .img from your computer Selecione um .img personalizado do seu computador - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Insira primeiro uma pen USB que contenha imagens.<br>As imagens devem estar localizadas na pasta de raiz da pen USB. + Insira primeiro uma pen USB que contenha imagens.<br>As imagens devem estar localizadas na pasta de raiz da pen USB. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - O cartão SD está protegido contra a gravação.<br>Empurre o interrutor de bloqueio no lado esquerdo do cartão para cima e tente novamente. + O cartão SD está protegido contra a gravação.<br>Empurre o interrutor de bloqueio no lado esquerdo do cartão para cima e tente novamente. diff --git a/src/i18n/rpi-imager_ru.ts b/src/i18n/rpi-imager_ru.ts index 6efad45b..299c550a 100644 --- a/src/i18n/rpi-imager_ru.ts +++ b/src/i18n/rpi-imager_ru.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Ошибка извлечения архива: %1 - + Error mounting FAT32 partition Ошибка подключения раздела FAT32 - + Operating system did not mount FAT32 partition Операционная система не подключила раздел FAT32 - + Error changing to directory '%1' Ошибка перехода в каталог «%1» @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive отключение диска - + opening drive открытие диска - + Error running diskpart: %1 Ошибка выполнения diskpart: %1 - + Error removing existing partitions Ошибка удаления существующих разделов - + Authentication cancelled Аутентификация отменена - + Error running authopen to gain access to disk device '%1' Ошибка выполнения authopen для получения доступа к дисковому устройству «%1» - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Убедитесь, что в параметрах настройки конфиденциальности (privacy settings) в разделе «файлы и папки» («files and folders») программе Raspberry Pi Imager разрешён доступ к съёмным томам (removable volumes). Либо можно предоставить программе доступ ко всему диску (full disk access). - + Cannot open storage device '%1'. Не удалось открыть запоминающее устройство «%1». - + discarding existing data on drive удаление существующих данных на диске - + zeroing out first and last MB of drive обнуление первого и последнего мегабайта диска - + Write error while zero'ing out MBR Ошибка записи при обнулении MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Ошибка записи при попытке обнулить последнюю часть карты.<br>Заявленный картой объём может не соответствовать действительности (возможно, карта является поддельной). - + starting download начало загрузки - + Error downloading: %1 Ошибка загрузки: %1 - + Access denied error while writing file to disk. Ошибка доступа при записи файла на диск. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Похоже, что включён контролируемый доступ к папкам (Controlled Folder Access). Добавьте rpi-imager.exe и fat32format.exe в список разрешённых программ и попробуйте снова. - + Error writing file to disk Ошибка записи файла на диск - + Download corrupt. Hash does not match Загруженные данные повреждены. Хэш не совпадает - - + + Error writing to storage (while flushing) Ошибка записи на запоминающее устройство (при сбросе) - - + + Error writing to storage (while fsync) Ошибка записи на запоминающее устройство (при выполнении fsync) - + Error writing first block (partition table) Ошибка записи первого блока (таблица разделов) - + Error reading from storage.<br>SD card may be broken. Ошибка чтения с запоминающего устройства.<br>SD-карта может быть повреждена. - + Verifying write failed. Contents of SD card is different from what was written to it. Ошибка по результатам проверки записи. Содержимое SD-карты отличается от того, что было на неё записано. - + Customizing image Настройка образа @@ -250,40 +250,105 @@ Для этой платформы не реализовано форматирование + + DstPopup + + + Storage + Запоминающее устройство + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Подключено как %1 + + + + GB + + + + + [WRITE PROTECTED] + [ЗАЩИЩЕНО ОТ ЗАПИСИ] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD-карта защищена от записи.<br>Переместите вверх блокировочный переключатель, расположенный с левой стороны карты, и попробуйте ещё раз. + + + + HWListModel + + + CHOOSE DEVICE + ВЫБРАТЬ УСТРОЙСТВО + + + + HwPopup + + + Raspberry Pi Device + Устройство Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Недостаточная ёмкость запоминающего устройства.<br>Требуется не менее %1 ГБ. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Входной файл не представляет собой корректный образ диска.<br>Размер файла %1 не кратен 512 байтам. - + Downloading and writing image Загрузка и запись образа - + Select image Выбор образа - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? Указать пароль от WiFi автоматически (взяв его из системной цепочки ключей)? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file открытие файла образа - + Error opening image file Ошибка открытия файла образа @@ -304,30 +369,163 @@ MsgPopup - + NO НЕТ - + YES ДА - + CONTINUE ПРОДОЛЖИТЬ - + QUIT ВЫЙТИ + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Операционная система + + + + Back + Назад + + + + Go back to main menu + Вернуться в главное меню + + + + Released: %1 + Дата выпуска: %1 + + + + Cached on your computer + Кэш на компьютере + + + + Local file + Локальный файл + + + + Online - %1 GB download + Онлайн — объём загрузки составляет %1 ГБ + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Сначала подключите USB-накопитель с образами.<br>Образы должны находиться в корневой папке USB-накопителя. + + + + OptionsGeneralTab + + + Set hostname: + Имя хоста: + + + + Set username and password + Указать имя пользователя и пароль + + + + Username: + Имя пользователя: + + + + + Password: + Пароль: + + + + Configure wireless LAN + Настроить Wi-Fi + + + + SSID: + SSID: + + + + Show password + Показывать пароль + + + + Hidden SSID + Скрытый идентификатор SSID + + + + Wireless LAN country: + Страна Wi-Fi: + + + + Set locale settings + Указать параметры региона + + + + Time zone: + Часовой пояс: + + + + Keyboard layout: + Раскладка клавиатуры: + + + + OptionsMiscTab + + + Play sound when finished + Воспроизводить звук после завершения + + + + Eject media when finished + Извлекать носитель после завершения + + + + Enable telemetry + Включить телеметрию + + OptionsPopup - + OS Customization @@ -340,123 +538,102 @@ для постоянного использования - + General Общие - + Services Службы - + Options Параметры - Set hostname: - Имя хоста: + Имя хоста: - Set username and password - Указать имя пользователя и пароль + Указать имя пользователя и пароль - Username: - Имя пользователя: + Имя пользователя: - - Password: - Пароль: + Пароль: - Configure wireless LAN - Настроить Wi-Fi + Настроить Wi-Fi - SSID: - SSID: + SSID: - Show password - Показывать пароль + Показывать пароль - Hidden SSID - Скрытый идентификатор SSID + Скрытый идентификатор SSID - Wireless LAN country: - Страна Wi-Fi: + Страна Wi-Fi: - Set locale settings - Указать параметры региона + Указать параметры региона - Time zone: - Часовой пояс: + Часовой пояс: - Keyboard layout: - Раскладка клавиатуры: + Раскладка клавиатуры: - Enable SSH - Включить SSH + Включить SSH - Use password authentication - Использовать аутентификацию по паролю + Использовать аутентификацию по паролю - Allow public-key authentication only - Разрешить только аутентификацию с использованием открытого ключа + Разрешить только аутентификацию с использованием открытого ключа - Set authorized_keys for '%1': - authorized_keys для «%1»: + authorized_keys для «%1»: - RUN SSH-KEYGEN - ВЫПОЛНИТЬ SSH-KEYGEN + ВЫПОЛНИТЬ SSH-KEYGEN - Play sound when finished - Воспроизводить звук после завершения + Воспроизводить звук после завершения - Eject media when finished - Извлекать носитель после завершения + Извлекать носитель после завершения - Enable telemetry - Включить телеметрию + Включить телеметрию - + SAVE СОХРАНИТЬ @@ -465,10 +642,48 @@ Постоянные параметры + + OptionsServicesTab + + + Enable SSH + Включить SSH + + + + Use password authentication + Использовать аутентификацию по паролю + + + + Allow public-key authentication only + Разрешить только аутентификацию с использованием открытого ключа + + + + Set authorized_keys for '%1': + authorized_keys для «%1»: + + + + Delete Key + + + + + RUN SSH-KEYGEN + ВЫПОЛНИТЬ SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Внутреннее устройство чтения SD-карт @@ -476,32 +691,27 @@ UseSavedSettingsPopup - - Use OS customization? - - - - + Would you like to apply OS customization settings? - + NO НЕТ - + NO, CLEAR SETTINGS НЕТ, ОЧИСТИТЬ ПАРАМЕТРЫ - + YES ДА - + EDIT SETTINGS ИЗМЕНИТЬ ПАРАМЕТРЫ @@ -509,62 +719,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager, версия %1 - - + Raspberry Pi Device Устройство Raspberry Pi - CHOOSE DEVICE - ВЫБРАТЬ УСТРОЙСТВО + ВЫБРАТЬ УСТРОЙСТВО - + Select this button to choose your target Raspberry Pi Нажмите эту кнопку, чтобы выбрать целевое устройство Raspberry Pi - - + Operating System Операционная система - - + + CHOOSE OS ВЫБРАТЬ ОС - + Select this button to change the operating system Нажмите эту кнопку, чтобы изменить операционную систему - - + Storage Запоминающее устройство - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE ВЫБРАТЬ ЗАПОМ. УСТРОЙСТВО @@ -573,40 +774,40 @@ ЗАПИСАТЬ - + Select this button to change the destination storage device Нажмите эту кнопку, чтобы изменить целевое запоминающее устройство - + CANCEL WRITE ОТМЕНИТЬ ЗАПИСЬ - - + + Cancelling... Отмена... - + CANCEL VERIFY ОТМЕНИТЬ ПРОВЕРКУ - - - + + + Finalizing... Завершение... - + Next Далее - + Select this button to start writing the image Нажмите эту кнопку, чтобы начать запись образа @@ -615,22 +816,22 @@ Нажмите эту кнопку для получения доступа к дополнительным параметрам - + Using custom repository: %1 Использование настраиваемого репозитория: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Навигация с помощью клавиатуры: клавиша <Tab> перемещает на следующую кнопку, клавиша <Пробел> нажимает кнопку/выбирает элемент, клавиши со <стрелками вверх/вниз> перемещают выше/ниже в списке - + Language: Язык: - + Keyboard: Клавиатура: @@ -639,84 +840,73 @@ Модель Pi: - [ All ] - [ Все ] + [ Все ] - Back - Назад + Назад - Go back to main menu - Вернуться в главное меню + Вернуться в главное меню - Released: %1 - Дата выпуска: %1 + Дата выпуска: %1 - Cached on your computer - Кэш на компьютере + Кэш на компьютере - Local file - Локальный файл + Локальный файл - Online - %1 GB download - Онлайн — объём загрузки составляет %1 ГБ + Онлайн — объём загрузки составляет %1 ГБ - - - Mounted as %1 - Подключено как %1 + Подключено как %1 - [WRITE PROTECTED] - [ЗАЩИЩЕНО ОТ ЗАПИСИ] + [ЗАЩИЩЕНО ОТ ЗАПИСИ] - + Are you sure you want to quit? Действительно выполнить выход? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Программа Raspberry Pi Imager всё ещё занята.<br>Действительно выполнить выход из неё? - + Warning Внимание - + Preparing to write... Подготовка к записи… - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Все существующие на «%1» данные будут стёрты.<br>Продолжить? - + Update available Доступно обновление - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Доступна новая версия Imager.<br>Перейти на веб-сайт для её загрузки? @@ -725,75 +915,72 @@ Ошибка загрузки списка ОС из Интернета - + Writing... %1% Запись... %1% - + Verifying... %1% Проверка... %1% - + Preparing to write... (%1) Подготовка к записи... (%1) - + Error Ошибка - + Write Successful Запись успешно выполнена - - + + Erase Стереть - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader Данные на <b>%1</b> стёрты<br><br>Теперь можно извлечь SD-карту из устройства чтения - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader Запись <b>%1</b> на <b>%2</b> выполнена<br><br>Теперь можно извлечь SD-карту из устройства чтения - Error parsing os_list.json - Ошибка синтаксического анализа os_list.json + Ошибка синтаксического анализа os_list.json - + Format card as FAT32 Отформатировать карту как FAT32 - + Use custom Использовать настраиваемый образ - + Select a custom .img from your computer Выбрать настраиваемый файл .img на компьютере - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Сначала подключите USB-накопитель с образами.<br>Образы должны находиться в корневой папке USB-накопителя. + Сначала подключите USB-накопитель с образами.<br>Образы должны находиться в корневой папке USB-накопителя. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD-карта защищена от записи.<br>Переместите вверх блокировочный переключатель, расположенный с левой стороны карты, и попробуйте ещё раз. + SD-карта защищена от записи.<br>Переместите вверх блокировочный переключатель, расположенный с левой стороны карты, и попробуйте ещё раз. diff --git a/src/i18n/rpi-imager_sk.ts b/src/i18n/rpi-imager_sk.ts index 79ae3df4..27efad49 100644 --- a/src/i18n/rpi-imager_sk.ts +++ b/src/i18n/rpi-imager_sk.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Chyba pri rozbaľovaní archívu: %1 - + Error mounting FAT32 partition Chyba pri pripájaní partície FAT32 - + Operating system did not mount FAT32 partition Operačný systém nepripojil partíciu FAT32 - + Error changing to directory '%1' Chyba pri vstupe do adresára '%1' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive odpájam jednotku - + opening drive otváram jednotku - + Error running diskpart: %1 Chyba počas behu diskpart: %1 - + Error removing existing partitions Chyba pri odstraňovaní existujúcich partiícií - + Authentication cancelled Zrušená autentifikácia - + Error running authopen to gain access to disk device '%1' Chyba pri spúšťaní authopen v snahe o získanie prístupu na diskové zariadenie '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Preverte, prosím, či má 'Raspberry Pi Imager' prístup k 'vymeniteľným nosičom' v nastaveniach súkromia (pod 'súbormi a priečinkami', prípadne mu udeľte 'plný prístup k diskom'). - + Cannot open storage device '%1'. Nepodarilo sa otvoriť zariadenie úložiska '%1'. - + discarding existing data on drive odstraňujem existujúce údaje na jednotke - + zeroing out first and last MB of drive prepisujem prvý a posledný megabajt na jednotke - + Write error while zero'ing out MBR Chyba zápisu pri prepisovaní MBR nulami - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Chyba zápisu pri prepisovaní poslednej časti karty nulami.<br>Karta pravdepodobne udáva nesprávnu kapacitu (a môže byť falošná). - + starting download začína sťahovanie - + Error downloading: %1 Chyba pri sťahovaní: %1 - + Access denied error while writing file to disk. Odopretý prístup pri zápise súboru na disk. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Vyzerá, že máte zapnutý Controlled Folder Access. Pridajte, prosím, rpi-imager.exe a fat32format.exe do zoznamu povolených aplikácií a skúste to znovu. - + Error writing file to disk Chyba pri zápise na disk - + Download corrupt. Hash does not match Stiahnutý súbor je poškodený. Kontrolný súčet nesedí - - + + Error writing to storage (while flushing) Chyba pri zápise na úložisko (počas volania flush) - - + + Error writing to storage (while fsync) Chyba pri zápise na úložisko (počas volania fsync) - + Error writing first block (partition table) Chyba pri zápise prvého bloku (tabuľky partícií) - + Error reading from storage.<br>SD card may be broken. Chyba pri čítaní z úložiska.<br>Karta SD môže byť poškodená. - + Verifying write failed. Contents of SD card is different from what was written to it. Overovanie zápisu skončilo s chybou. Obsah karty SD sa nezhoduje s tým, čo na ňu bolo zapísané. - + Customizing image Upravujem obraz @@ -250,40 +250,105 @@ Formátovanie nie je na tejto platforme implementované + + DstPopup + + + Storage + SD karta + + + + No storage devices found + Nenašli sa žiadne úložné zariadenia + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Pripojená ako %1 + + + + GB + + + + + [WRITE PROTECTED] + [OCHRANA PROTI ZÁPISU] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD karta je chránená proti zápisu.<br>Presuňte prepínač zámku na ľavej strane karty smerom hore a skúste to znova. + + + + HWListModel + + + CHOOSE DEVICE + VYBERTE ZARIADENIE + + + + HwPopup + + + Raspberry Pi Device + Zariadenie Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Kapacita úložiska je nedostatočná<br>Musí byť aspoň %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Vstupný súbor nie je platným obrazom disku.<br>Veľkosť súboru %1 bajtov nie je násobkom 512 bajtov. - + Downloading and writing image Sťahujem a zapisujem obraz - + Select image Vyberte obraz - + Error synchronizing time. Trying again in 3 seconds Chyba pri synchronizácii času. Vyskúšam to znova o 3 sekundy - + STP is enabled on your Ethernet switch. Getting IP will take long time. Na vašom sieťovom prepínači je povolený protokol STP. Získanie IP bude trvať dlhý čas. - + Would you like to prefill the wifi password from the system keychain? Chcete predvyplniť heslo pre wifi zo systémovej kľúčenky? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file otváram súbor s obrazom - + Error opening image file Chyba pri otváraní súboru s obrazom @@ -304,30 +369,163 @@ MsgPopup - + NO NIE - + YES ÁNO - + CONTINUE POKRAČOVAŤ - + QUIT UKONČIŤ + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Operačný systém + + + + Back + Späť + + + + Go back to main menu + Prejsť do hlavnej ponuky + + + + Released: %1 + Vydané: %1 + + + + Cached on your computer + Uložené na počítači + + + + Local file + Miestny súbor + + + + Online - %1 GB download + Online %1 GB na stiahnutie + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Najprv pripojte USB kľúč, ktorý obsahuje diskové obrazy.<br>Obrazy sa musia nachádzať v koreňovom priečinku USB kľúča. + + + + OptionsGeneralTab + + + Set hostname: + Nastaviť meno počítača (hostname): + + + + Set username and password + Nastaviť meno používateľa a heslo + + + + Username: + Meno používateľa: + + + + + Password: + Heslo: + + + + Configure wireless LAN + Nastaviť wifi + + + + SSID: + SSID: + + + + Show password + Zobraziť heslo + + + + Hidden SSID + Skryté SSID + + + + Wireless LAN country: + Wifi krajina: + + + + Set locale settings + Nastavenia miestnych zvyklostí + + + + Time zone: + Časové pásmo: + + + + Keyboard layout: + Rozloženie klávesnice: + + + + OptionsMiscTab + + + Play sound when finished + Po skončení prehrať zvuk + + + + Eject media when finished + Po skončení vysunúť médium + + + + Enable telemetry + Povoliť telemetriu + + OptionsPopup - + OS Customization Úpravy OS @@ -340,123 +538,102 @@ použiť vždy - + General Všeobecné - + Services Služby - + Options Možnosti - Set hostname: - Nastaviť meno počítača (hostname): + Nastaviť meno počítača (hostname): - Set username and password - Nastaviť meno používateľa a heslo + Nastaviť meno používateľa a heslo - Username: - Meno používateľa: + Meno používateľa: - - Password: - Heslo: + Heslo: - Configure wireless LAN - Nastaviť wifi + Nastaviť wifi - SSID: - SSID: + SSID: - Show password - Zobraziť heslo + Zobraziť heslo - Hidden SSID - Skryté SSID + Skryté SSID - Wireless LAN country: - Wifi krajina: + Wifi krajina: - Set locale settings - Nastavenia miestnych zvyklostí + Nastavenia miestnych zvyklostí - Time zone: - Časové pásmo: + Časové pásmo: - Keyboard layout: - Rozloženie klávesnice: + Rozloženie klávesnice: - Enable SSH - Povoliť SSH + Povoliť SSH - Use password authentication - Použiť heslo na prihlásenie + Použiť heslo na prihlásenie - Allow public-key authentication only - Povoliť iba prihlásenie pomocou verejného kľúča + Povoliť iba prihlásenie pomocou verejného kľúča - Set authorized_keys for '%1': - Nastaviť authorized_keys pre '%1': + Nastaviť authorized_keys pre '%1': - RUN SSH-KEYGEN - SPUSTIŤ SSH-KEYGEN + SPUSTIŤ SSH-KEYGEN - Play sound when finished - Po skončení prehrať zvuk + Po skončení prehrať zvuk - Eject media when finished - Po skončení vysunúť médium + Po skončení vysunúť médium - Enable telemetry - Povoliť telemetriu + Povoliť telemetriu - + SAVE ULOŽIŤ @@ -481,10 +658,48 @@ Trvalé nastavenia + + OptionsServicesTab + + + Enable SSH + Povoliť SSH + + + + Use password authentication + Použiť heslo na prihlásenie + + + + Allow public-key authentication only + Povoliť iba prihlásenie pomocou verejného kľúča + + + + Set authorized_keys for '%1': + Nastaviť authorized_keys pre '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + SPUSTIŤ SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Interná čítačka SD kariet @@ -492,32 +707,31 @@ UseSavedSettingsPopup - Use OS customization? - Použiť úpravy OS? + Použiť úpravy OS? - + Would you like to apply OS customization settings? Chceli by ste použiť nastavenia, ktoré upravujú operačný systém? - + NO NIE - + NO, CLEAR SETTINGS NIE, VYČISTIŤ NASTAVENIA - + YES ÁNO - + EDIT SETTINGS UPRAVIŤ NASTAVENIA @@ -525,52 +739,48 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Zariadenie Raspberry Pi - CHOOSE DEVICE - VYBERTE ZARIADENIE + VYBERTE ZARIADENIE - + Select this button to choose your target Raspberry Pi Vyberte toto tlačidlo pre výber cieľového Raspberry Pi - - + Operating System Operačný systém - - + + CHOOSE OS VYBERTE OS - + Select this button to change the operating system Pre zmenu operačného systému kliknite na toto tlačidlo - - + Storage SD karta - - + + CHOOSE STORAGE VYBERTE SD KARTU @@ -579,40 +789,40 @@ ZAPÍSAŤ - + Select this button to change the destination storage device Pre zmenu cieľového zariadenia úložiska kliknite na toto tlačidlo - + CANCEL WRITE ZRUŠIŤ ZÁPIS - - + + Cancelling... Ruším operáciu... - + CANCEL VERIFY ZRUŠIŤ OVEROVANIE - - - + + + Finalizing... Ukončujem... - + Next Ďalej - + Select this button to start writing the image Kliknutím na toto tlačidlo spustíte zápis @@ -621,114 +831,102 @@ Použite toto tlačidlo na prístup k pokročilým nastaveniam - + Using custom repository: %1 Používa sa vlastný repozitár: %1 - + Network not ready yet Sieť ešte nie je pripravená - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Ovládanie pomocou klávesnice: <tabulátor> prechod na ďalšie tlačidlo <medzerník> stlačenie tlačidla/výber položky <šípka hore/dole> posun hore/dole v zoznamoch - + Language: Jazyk: - + Keyboard: Klávesnica: - [ All ] - [ Všetky ] + [ Všetky ] - Back - Späť + Späť - Go back to main menu - Prejsť do hlavnej ponuky + Prejsť do hlavnej ponuky - Released: %1 - Vydané: %1 + Vydané: %1 - Cached on your computer - Uložené na počítači + Uložené na počítači - Local file - Miestny súbor + Miestny súbor - Online - %1 GB download - Online %1 GB na stiahnutie + Online %1 GB na stiahnutie - No storage devices found - Nenašli sa žiadne úložné zariadenia + Nenašli sa žiadne úložné zariadenia - - - Mounted as %1 - Pripojená ako %1 + Pripojená ako %1 - [WRITE PROTECTED] - [OCHRANA PROTI ZÁPISU] + [OCHRANA PROTI ZÁPISU] - + Are you sure you want to quit? Skutočne chcete skončiť? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager ešte neskončil.<br>Ste si istý, že chcete skončiť? - + Warning Varovanie - + Preparing to write... Príprava zápisu... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Všetky existujúce dáta na '%1' budú odstránené.<br>Naozaj chcete pokračovať? - + Update available Je dostupná aktualizácia - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Je dostupná nová verzia Imagera.<br>Chcete prejsť na webovú stránku s programom a stiahnuť ho? @@ -737,75 +935,72 @@ Chyba pri sťahovaní zoznamu OS z Internetu - + Writing... %1% Zapisujem... %1% - + Verifying... %1% Overujem... %1% - + Preparing to write... (%1) Príprava zápisu... (%1) - + Error Chyba - + Write Successful Zápis úspešne skončil - - + + Erase Vymazať - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> bola vymazaná<br><br>Teraz môžete odstrániť SD kartu z čítačky - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> bol zapísaný na <b>%2</b><br><br>Teraz môžete odstrániť SD kartu z čítačky - Error parsing os_list.json - Chyba pri spracovaní os_list.json + Chyba pri spracovaní os_list.json - + Format card as FAT32 Formátovať kartu ako FAT32 - + Use custom Použiť vlastný - + Select a custom .img from your computer Použiť vlastný súbor img. na Vašom počítači - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Najprv pripojte USB kľúč, ktorý obsahuje diskové obrazy.<br>Obrazy sa musia nachádzať v koreňovom priečinku USB kľúča. + Najprv pripojte USB kľúč, ktorý obsahuje diskové obrazy.<br>Obrazy sa musia nachádzať v koreňovom priečinku USB kľúča. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD karta je chránená proti zápisu.<br>Presuňte prepínač zámku na ľavej strane karty smerom hore a skúste to znova. + SD karta je chránená proti zápisu.<br>Presuňte prepínač zámku na ľavej strane karty smerom hore a skúste to znova. Select this button to change the destination SD card diff --git a/src/i18n/rpi-imager_sl.ts b/src/i18n/rpi-imager_sl.ts index 2fb4f270..963886b2 100644 --- a/src/i18n/rpi-imager_sl.ts +++ b/src/i18n/rpi-imager_sl.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Napaka razširjanja arhiva: %1 - + Error mounting FAT32 partition Napaka priklopa FAT32 particije - + Operating system did not mount FAT32 partition Operacijski sistem, ni priklopil FAT32 particije - + Error changing to directory '%1' Napaka spremembe direktorija '%1%' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive - + opening drive Odpiranje pogona - + Error running diskpart: %1 Napaka zagona diskpart: %1 - + Error removing existing partitions Napaka odstranjevanja obstoječih particij - + Authentication cancelled Avtentifikacija preklicana - + Error running authopen to gain access to disk device '%1' Napaka zagona authopen za pridobitev dostopa do naprave diska '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Prosim preverite če ima 'Raspberry Pi Imager' pravico dostopa do 'odstranljivih mediev' pod nastavitvami zasebnosti (pod 'datoteke in mape' ali pa mu dajte 'popolni dostop do diska'). - + Cannot open storage device '%1'. Ne morem odpreti diska '%1'. - + discarding existing data on drive Brisanje obstoječih podatkov na disku - + zeroing out first and last MB of drive Ničenje prvega in zadnjega MB diska - + Write error while zero'ing out MBR Napaka zapisovanja med ničenjem MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Napaka ničenja zadnjega dela diska.<br>Disk morebiti sporoča napačno velikost(možen ponaredek). - + starting download Začetek prenosa - + Error downloading: %1 Napaka prenosa:%1 - + Access denied error while writing file to disk. Napaka zavrnitve dostopa med pisanjem na disk. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Izgleda, da jevklopljen nadzor dostopa do map. Prosim dodajte oba rpi-imager.exe in fat32format.exe na seznam dovoljenih aplikacij in poizkusite znova. - + Error writing file to disk Napaka pisanja na disk - + Download corrupt. Hash does not match Prenos poškodovan.Hash se ne ujema - - + + Error writing to storage (while flushing) Napaka pisanja na disk (med brisanjem) - - + + Error writing to storage (while fsync) Napaka pisanja na disk (med fsync) - + Error writing first block (partition table) Napaka pisanja prvega bloka (particijska tabela) - + Error reading from storage.<br>SD card may be broken. Napaka branja iz diska.<br>SD kartica/disk je mogoče v okvari. - + Verifying write failed. Contents of SD card is different from what was written to it. Preverjanje pisanja spodletelo. Vsebina diska je drugačna, od tega, kar je bilo nanj zapisano. - + Customizing image Prilagajanje slike diska @@ -250,40 +250,105 @@ Formatiranje ni implemntirano za to platformo + + DstPopup + + + Storage + SD kartica ali USB disk + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Priklopljen kot %1 + + + + GB + + + + + [WRITE PROTECTED] + [ZAŠČITENO PRED PISANJEM] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD kartica je zaščitena pred pisanjem.<br>Premaknite stikalo zaklepanja, na levi strani kartice in poizkusite znova. + + + + HWListModel + + + CHOOSE DEVICE + + + + + HwPopup + + + Raspberry Pi Device + + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Kapaciteta diska ni zadostna.<br>Biti mora vsaj %1 GB. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Vhodna datoteka ni veljavna slika diska.<br>Velikost datoteke %1 bajtov ni večkratnik od 512 bajtov. - + Downloading and writing image Prenašanje in zapisovanje slike - + Select image Izberite sliko - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? A bi želeli uporabiti WiFi geslo iz kjučev tega sistema? @@ -291,12 +356,12 @@ LocalFileExtractThread - + opening image file Odpiranje slike diska - + Error opening image file Napaka odpiranja slike diska @@ -304,159 +369,255 @@ MsgPopup - + NO NE - + YES DA - + CONTINUE NADALJUJ - + QUIT IZHOD - OptionsPopup + OSListModel - - OS Customization + + Recommended + + + OSPopup - for this session only - samo za to sejo + + Operating System + Operacijski Sistem - to always use - vedno + + Back + Nazaj - - General - + + Go back to main menu + Pojdi nazaj na glavni meni - - Services - + + Released: %1 + Izdano: %1 - - Options - + + Cached on your computer + Predpolnjeno na vaš računalnik + + + + Local file + Lokalna datoteka + + + + Online - %1 GB download + Iz spleta - %1 GB prenos + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Najprej prikopite USB disk, ki vsebuje slike diskov.<br>Slike diskov se morajo nahajati v korenski mapi USB diska. + + + OptionsGeneralTab - + Set hostname: - Ime naprave: + Ime naprave: - + Set username and password - + Username: - - + + Password: - Geslo: + Geslo: - + Configure wireless LAN - Nastavi WiFi + Nastavi WiFi - + SSID: - SSID: + SSID: - + Show password - Pokaži geslo + Pokaži geslo - + Hidden SSID - + Wireless LAN country: - WiFi je v državi: + WiFi je v državi: - + Set locale settings - Nastavitve jezika + Nastavitve jezika - + Time zone: - Časovni pas: + Časovni pas: - + Keyboard layout: - Razporeditev tipkovnice: + Razporeditev tipkovnice: + + + + OptionsMiscTab + + + Play sound when finished + Zaigraj zvok, ko končaš + + + + Eject media when finished + Izvrzi medij, ko končaš + + + + Enable telemetry + Omogoči telemetrijo + + + + OptionsPopup + + + OS Customization + + + + for this session only + samo za to sejo + + + to always use + vedno + + + + General + + + + + Services + + + + + Options + + + + Set hostname: + Ime naprave: + + + Password: + Geslo: + + + Configure wireless LAN + Nastavi WiFi + + + SSID: + SSID: + + + Show password + Pokaži geslo + + + Wireless LAN country: + WiFi je v državi: + + + Set locale settings + Nastavitve jezika + + + Time zone: + Časovni pas: + + + Keyboard layout: + Razporeditev tipkovnice: - Enable SSH - Omogoči SSH + Omogoči SSH - Use password authentication - Uporabi geslo za avtentifikacijo + Uporabi geslo za avtentifikacijo - Allow public-key authentication only - Dovoli le avtentifikacijo z javnim kjučem + Dovoli le avtentifikacijo z javnim kjučem - Set authorized_keys for '%1': - Nastavi authorized_keys za '%1': + Nastavi authorized_keys za '%1': - - RUN SSH-KEYGEN - - - - Play sound when finished - Zaigraj zvok, ko končaš + Zaigraj zvok, ko končaš - Eject media when finished - Izvrzi medij, ko končaš + Izvrzi medij, ko končaš - Enable telemetry - Omogoči telemetrijo + Omogoči telemetrijo - + SAVE SHRANI @@ -481,10 +642,48 @@ Trajne nastavitve + + OptionsServicesTab + + + Enable SSH + Omogoči SSH + + + + Use password authentication + Uporabi geslo za avtentifikacijo + + + + Allow public-key authentication only + Dovoli le avtentifikacijo z javnim kjučem + + + + Set authorized_keys for '%1': + Nastavi authorized_keys za '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + + + + + Add SSH Key + + + QObject - + Internal SD card reader Vgrajeni čitalec SD kartic @@ -492,32 +691,27 @@ UseSavedSettingsPopup - - Use OS customization? - - - - + Would you like to apply OS customization settings? - + NO NE - + NO, CLEAR SETTINGS NE, POBRIŠI NASTAVITVE - + YES DA - + EDIT SETTINGS UREDI NASTAVITVE @@ -525,52 +719,44 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device - - CHOOSE DEVICE - - - - + Select this button to choose your target Raspberry Pi - - + Operating System Operacijski Sistem - - + + CHOOSE OS IZBERI OS - + Select this button to change the operating system Izberite ta gumb za menjavo operacijskega sistema - - + Storage SD kartica ali USB disk - - + + CHOOSE STORAGE IZBERI DISK @@ -579,152 +765,132 @@ ZAPIŠI - + Select this button to change the destination storage device Uporabite ta gumb za spremembo ciljnega diska - + CANCEL WRITE PREKINI ZAPISOVANJE - - + + Cancelling... Prekinjam... - + CANCEL VERIFY PREKINI PREVERJANJE - - - + + + Finalizing... Zakjučujem... - + Next - + Select this button to start writing the image Izberite za gumb za začetek pisanja slike diska - + Using custom repository: %1 Uporabljam repozitorij po meri: %1 - + Network not ready yet - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Navigacija s tipkovnico: <tab> pojdi na naslednji gumb <preslednica> pritisni gumb/izberi element <puščica gor/dol> premakni gor/dol po seznamu - + Language: Jezik: - + Keyboard: Tipkovnica: - - [ All ] - - - - Back - Nazaj + Nazaj - Go back to main menu - Pojdi nazaj na glavni meni + Pojdi nazaj na glavni meni - Released: %1 - Izdano: %1 + Izdano: %1 - Cached on your computer - Predpolnjeno na vaš računalnik + Predpolnjeno na vaš računalnik - Local file - Lokalna datoteka + Lokalna datoteka - Online - %1 GB download - Iz spleta - %1 GB prenos + Iz spleta - %1 GB prenos - - No storage devices found - - - - - - Mounted as %1 - Priklopljen kot %1 + Priklopljen kot %1 - [WRITE PROTECTED] - [ZAŠČITENO PRED PISANJEM] + [ZAŠČITENO PRED PISANJEM] - + Are you sure you want to quit? A ste prepričani, da želite končat? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager je še vedno zaposlen.<br>A ste prepričani, da želite končati? - + Warning Opozorilo - + Preparing to write... Priprava na pisanje... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? VSI obstoječi podatki na '%1' bodo izbrisani.<br>A ste prepričani, da želite nadaljevati? - + Update available Posodobitev na voljo - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Na voljo je nova verzija tega programa. <br>Želite obiskati spletno stran za prenos? @@ -733,75 +899,72 @@ Napaka prenosa seznama OS iz interneta - + Writing... %1% Pišem...%1% - + Verifying... %1% Preverjam... %1% - + Preparing to write... (%1) Priprava na zapisovanje... (%1) - + Error Napaka - + Write Successful Zapisovanje uspešno - - + + Erase Odstrani - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> je pobrisan<br><br>Sedaj lahko odstranite SD kartico iz čitalca oz iztaknete USB disk - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> je zapisan na <b>%2</b><br><br>Sedaj lahko odstranite SD kartico iz čitalca oz iztaknete USB disk - Error parsing os_list.json - Napaka procesiranja os_list.json + Napaka procesiranja os_list.json - + Format card as FAT32 Formatiraj disk v FAT32 - + Use custom Uporabi drugo - + Select a custom .img from your computer Izberite drug .img iz vašega računalnika - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Najprej prikopite USB disk, ki vsebuje slike diskov.<br>Slike diskov se morajo nahajati v korenski mapi USB diska. + Najprej prikopite USB disk, ki vsebuje slike diskov.<br>Slike diskov se morajo nahajati v korenski mapi USB diska. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD kartica je zaščitena pred pisanjem.<br>Premaknite stikalo zaklepanja, na levi strani kartice in poizkusite znova. + SD kartica je zaščitena pred pisanjem.<br>Premaknite stikalo zaklepanja, na levi strani kartice in poizkusite znova. <b>%1</b> has been written to <b>%2</b> diff --git a/src/i18n/rpi-imager_tr.ts b/src/i18n/rpi-imager_tr.ts index dc5391f5..4e7ff160 100644 --- a/src/i18n/rpi-imager_tr.ts +++ b/src/i18n/rpi-imager_tr.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Arşiv çıkarılırken hata oluştu: %1 - + Error mounting FAT32 partition FAT32 bölümü bağlanırken hata oluştu - + Operating system did not mount FAT32 partition İşletim sistemi FAT32 bölümünü bağlamadı - + Error changing to directory '%1' Dizin değiştirirken hata oluştu '%1' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive - + opening drive sürücü açılıyor - + Error running diskpart: %1 Diskpart çalıştırılırken hata oluştu: %1 - + Error removing existing partitions Mevcut bölümler kaldırılırken hata oluştu - + Authentication cancelled Kimlik doğrulama iptal edildi - + Error running authopen to gain access to disk device '%1' '%1' disk aygıtına erişmek için authopen çalıştırılırken hata oluştu - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Lütfen 'Raspberry Pi Imager'ın gizlilik ayarlarında ('dosyalar ve klasörler' altında veya alternatif olarak 'tam disk erişimi') 'çıkarılabilir birimlere erişim' izin verilip verilmediğini doğrulayın. - + Cannot open storage device '%1'. Depolama cihazı açılamıyor '%1'. - + discarding existing data on drive sürücüdeki mevcut verileri sil - + zeroing out first and last MB of drive sürücünün ilk ve son MB'sini sıfırlama - + Write error while zero'ing out MBR MBR sıfırlanırken yazma hatası - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Kartın son kısmını sıfırlamaya çalışırken yazma hatası. Kart yanlış kapasitenin tanımını yapıyor olabilir (olası sahte bölüm boyutu tanımı) - + starting download indirmeye başlanıyor - + Error downloading: %1 İndirilirken hata oluştu: %1 - + Access denied error while writing file to disk. Dosyayı diske yazarken erişim reddedildi hatası - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Kontrollü Klasör Erişimi etkin görünüyor. Lütfen izin verilen uygulamalar listesine hem rpi-imager.exe'yi hem de fat32format.exe'yi ekleyin ve tekrar deneyin. - + Error writing file to disk Dosyayı diske yazma hatası - + Download corrupt. Hash does not match İndirme bozuk. Hash eşleşmiyor - - + + Error writing to storage (while flushing) Depolama alanına yazma hatası (flushing sırasında) - - + + Error writing to storage (while fsync) Depoya yazma hatası (fsync sırasında) - + Error writing first block (partition table) İlk bloğu yazma hatası (bölüm tablosu) - + Error reading from storage.<br>SD card may be broken. Depolamadan okuma hatası.<br>SD kart arızalı olabilir. - + Verifying write failed. Contents of SD card is different from what was written to it. Yazma doğrulanamadı. SD kartın içeriği, üzerine yazılandan farklı. - + Customizing image @@ -222,40 +222,105 @@ Bu platform için biçimlendirme uygulanmadı + + DstPopup + + + Storage + SD Kart + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + %1 olarak bağlandı. + + + + GB + + + + + [WRITE PROTECTED] + [YAZMA KORUMALI] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD kart yazma korumalı. <br> Kartın sol tarafındaki kilit anahtarını yukarı itin ve tekrar deneyin. + + + + HWListModel + + + CHOOSE DEVICE + + + + + HwPopup + + + Raspberry Pi Device + + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Depolama kapasitesi yeterince büyük değil.<br>En az %1 GB olması gerekiyor - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Giriş dosyası geçerli bir disk görüntüsü değil.<br>%1 bayt dosya boyutu 512 baytın katı değil. - + Downloading and writing image Görüntü indirme ve yazma - + Select image Imaj seç - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? @@ -263,12 +328,12 @@ LocalFileExtractThread - + opening image file imaj dosyası açılıyor - + Error opening image file Imaj dosyası açılırken hata oluştu @@ -276,159 +341,230 @@ MsgPopup - + NO HAYIR - + YES EVET - + CONTINUE DEVAM ET - + QUIT - OptionsPopup + OSListModel - - OS Customization + + Recommended + + + OSPopup - - General - + + Operating System + İşletim sistemi - - Services - + + Back + Geri - - Options - + + Go back to main menu + Ana menüye dön + + + + Released: %1 + Yayın: %1 + + + + Cached on your computer + Bilgisayarınızda önbelleğe alındı + + + + + Local file + Yerel dosya + + + + Online - %1 GB download + Çevrimiçi -%1 GB indir - + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Önce görüntüler içeren bir USB bellek bağlayın.<br> Görüntüler USB belleğin kök klasöründe bulunmalıdır. + + + + OptionsGeneralTab + + Set hostname: - + Set username and password - + Username: - - + + Password: - + Configure wireless LAN - + SSID: - + Show password - + Hidden SSID - + Wireless LAN country: - + Set locale settings - + Time zone: - + Keyboard layout: + + + OptionsMiscTab - - Enable SSH + + Play sound when finished - - Use password authentication + + Eject media when finished - - Allow public-key authentication only + + Enable telemetry + + + OptionsPopup - - Set authorized_keys for '%1': + + OS Customization - - RUN SSH-KEYGEN + + General - - Play sound when finished + + Services - - Eject media when finished + + Options - - Enable telemetry + + SAVE + + + OptionsServicesTab - - SAVE + + Enable SSH + + + + + Use password authentication + + + + + Allow public-key authentication only + + + + + Set authorized_keys for '%1': + + + + + Delete Key + + + + + RUN SSH-KEYGEN + + + + + Add SSH Key QObject - + Internal SD card reader Dahili SD kart okuyucu @@ -436,32 +572,27 @@ UseSavedSettingsPopup - - Use OS customization? - - - - + Would you like to apply OS customization settings? - + NO HAYIR - + NO, CLEAR SETTINGS - + YES EVET - + EDIT SETTINGS @@ -469,62 +600,49 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imaj Yöneticisi v%1 - - + Raspberry Pi Device - - CHOOSE DEVICE - - - - + Select this button to choose your target Raspberry Pi - - + Operating System İşletim sistemi - - + + CHOOSE OS İŞLETİM SİSTEMİ SEÇİN - + Select this button to change the operating system İşletim sistemini değiştirmek için bu düğmeyi seçin - - + Storage SD Kart - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE SD KART SEÇİN @@ -533,143 +651,128 @@ YAZ - + Select this button to change the destination storage device - + CANCEL WRITE YAZMAYI İPTAL ET - - + + Cancelling... İptal ediliyor... - + CANCEL VERIFY DOĞRULAMA İPTALİ - - - + + + Finalizing... Bitiriliyor... - + Next - + Select this button to start writing the image Görüntüyü yazmaya başlamak için bu düğmeyi seçin - + Using custom repository: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists - + Language: - + Keyboard: - - [ All ] - - - - Back - Geri + Geri - Go back to main menu - Ana menüye dön + Ana menüye dön - Released: %1 - Yayın: %1 + Yayın: %1 - Cached on your computer - Bilgisayarınızda önbelleğe alındı + Bilgisayarınızda önbelleğe alındı - Local file - Yerel dosya + Yerel dosya - Online - %1 GB download - Çevrimiçi -%1 GB indir + Çevrimiçi -%1 GB indir - - - Mounted as %1 - %1 olarak bağlandı. + %1 olarak bağlandı. - [WRITE PROTECTED] - [YAZMA KORUMALI] + [YAZMA KORUMALI] - + Are you sure you want to quit? Çıkmak istediğine emin misin? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager hala meşgul.<br>Çıkmak istediğinizden emin misiniz? - + Warning Uyarı - + Preparing to write... Yazdırmaya hazırlanıyor... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? '%1' üzerindeki mevcut tüm veriler silinecek.<br>Devam etmek istediğinizden emin misiniz? - + Update available Güncelleme bulunuyor - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Görüntüleyicinin daha yeni bir sürümü var. <br> İndirmek için web sitesini ziyaret etmek ister misiniz? @@ -678,75 +781,72 @@ İnternetten işletim sistemi listesi indirilirken hata oluştu - + Writing... %1% Yazılıyor... %1% - + Verifying... %1% Doğrulanıyor... %1% - + Preparing to write... (%1) Yazdırmaya hazırlanıyor... (%1) - + Error Hata - + Write Successful Başarılı Yazıldı - - + + Erase Sil - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> silindi <br><br> Artık SD kartı okuyucudan çıkarabilirsiniz - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> <b>%2</b><br><br> üzerine yazıldı. Artık SD kartı okuyucudan çıkarabilirsiniz - Error parsing os_list.json - os_list.json ayrıştırma hatası + os_list.json ayrıştırma hatası - + Format card as FAT32 Kartı FAT32 olarak biçimlendir - + Use custom Özel imaj kullan - + Select a custom .img from your computer Bilgisayarınızdan özel bir .img seçin - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Önce görüntüler içeren bir USB bellek bağlayın.<br> Görüntüler USB belleğin kök klasöründe bulunmalıdır. + Önce görüntüler içeren bir USB bellek bağlayın.<br> Görüntüler USB belleğin kök klasöründe bulunmalıdır. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD kart yazma korumalı. <br> Kartın sol tarafındaki kilit anahtarını yukarı itin ve tekrar deneyin. + SD kart yazma korumalı. <br> Kartın sol tarafındaki kilit anahtarını yukarı itin ve tekrar deneyin. Select this button to change the destination SD card diff --git a/src/i18n/rpi-imager_uk.ts b/src/i18n/rpi-imager_uk.ts index 7921546e..c4935ef0 100644 --- a/src/i18n/rpi-imager_uk.ts +++ b/src/i18n/rpi-imager_uk.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 Помилка розпакування архіва: %1 - + Error mounting FAT32 partition Помилка монтування FAT32 розділу - + Operating system did not mount FAT32 partition Операційна система не монтувала FAT32 розділ - + Error changing to directory '%1' Помилка при зміні каталогу на '%1' @@ -32,124 +32,124 @@ DownloadThread - + unmounting drive диск від'єднується - + opening drive диск відкривається - + Error running diskpart: %1 Помилка при виконанні diskpart: %1 - + Error removing existing partitions Помилка при видаленні існуючих розділів - + Authentication cancelled Аутентифікація скасована - + Error running authopen to gain access to disk device '%1' Помилка виконання authopen для отримання доступу до пристроя '%1' - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). Переконайтеся, що у Raspberry Pi Imager у налаштуваннях приватності (у розділі "файли та каталоги") є доступ до змінних розділів. Або дайте програмі доступ до усього диску. - + Cannot open storage device '%1'. Не вдалося відкрити накопичувач '%1'. - + discarding existing data on drive видалення існуючих даних на диску - + zeroing out first and last MB of drive обнулювання першого і останнього мегабайта диску - + Write error while zero'ing out MBR Помилка при обнулюванні MBR - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). Помилка запису під час обнулювання останнього розділу карти пам'яті.<br>Можливо заявлений об'єм карти не збігається з реальним (можливо карта є підробленою). - + starting download початок завантаження - + Error downloading: %1 Помилка завантаження: %1 - + Access denied error while writing file to disk. Помилка доступу при записі файлу на диск. - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. Схоже, що увімкнено контрольований доступ до каталогу (Controlled Folder Access). Додайте rpi-imager.exe і fat32format.exe в список виключення та спробуйте ще раз. - + Error writing file to disk Помилка запису файлу на диск - + Download corrupt. Hash does not match Завантаження пошкоджено. Хеш сума не збігається - - + + Error writing to storage (while flushing) Помилка запису на накопичувач (при скидуванні) - - + + Error writing to storage (while fsync) Помилка запису на накопичувач (при виконанні fsync) - + Error writing first block (partition table) Помилка під час запису першого блоку (таблиця розділів) - + Error reading from storage.<br>SD card may be broken. Помилка читання накопичувача.<br>SD-карта пам'яті може бути пошкоджена. - + Verifying write failed. Contents of SD card is different from what was written to it. Помилка перевірки запису. Зміст SD-карти пам'яті відрізняється від того, що було записано туди. - + Customizing image Налаштування образа @@ -214,40 +214,105 @@ Форматування не доступно на цій платформі + + DstPopup + + + Storage + Накопичувач + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + Примонтовано як %1 + + + + GB + + + + + [WRITE PROTECTED] + ЗАХИЩЕНО ВІД ЗАПИСУ + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD карта захищена від запису.<br>перетягніть перемикач на лівій стороні картки вгору, і спробуйте ще раз. + + + + HWListModel + + + CHOOSE DEVICE + ОБРАТИ ПРИСТРІЙ + + + + HwPopup + + + Raspberry Pi Device + Пристрій Raspberry Pi + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. Місця на накопичувачі недостатньо.<br>Треба, щоб було хоча б %1 ГБ. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. Обраний файл не є правильним образом диску.<br>Розмір файла %1 байт не є кратним 512 байт. - + Downloading and writing image Завантаження і запис образу - + Select image Обрати образ - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? Вказати пароль від Wi-Fi автоматично із системного ланцюга ключів? @@ -255,12 +320,12 @@ LocalFileExtractThread - + opening image file відкривання файлу образа - + Error opening image file Помилка при відкриванні файлу образу @@ -268,30 +333,163 @@ MsgPopup - + NO НІ - + YES ТАК - + CONTINUE ПРОДОВЖИТИ - + QUIT ВИЙТИ + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + Операційна система + + + + Back + Назад + + + + Go back to main menu + Повернутися у головне меню + + + + Released: %1 + Випущено: %1 + + + + Cached on your computer + Кешовано на вашому комп'ютері + + + + Local file + Локальний файл + + + + Online - %1 GB download + Онлайн - потрібно завантажити %1 ГБ + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Спочатку під'єднайте USB-накопичувач з образами.<br>Образи повинні знаходитися у корінному каталогу USB-накопичувача. + + + + OptionsGeneralTab + + + Set hostname: + Встановити ім'я хосту: + + + + Set username and password + Встановити ім'я користувача і пароль + + + + Username: + Ім'я користувача: + + + + + Password: + Пароль: + + + + Configure wireless LAN + Налаштувати бездротову LAN мережу + + + + SSID: + SSID: + + + + Show password + Показати пароль + + + + Hidden SSID + Прихована SSID + + + + Wireless LAN country: + Країна бездротової LAN мережі: + + + + Set locale settings + Змінити налаштування регіону + + + + Time zone: + Часова зона: + + + + Keyboard layout: + Розкладка клавіатури: + + + + OptionsMiscTab + + + Play sound when finished + Відтворити звук після завершення + + + + Eject media when finished + Витягнути накопичувач після завершення + + + + Enable telemetry + Увімкнути телеметрію + + OptionsPopup - + OS Customization Налаштування ОС @@ -308,123 +506,102 @@ для постійного використання - + General Загальні - + Services Сервіси - + Options Налаштування - Set hostname: - Встановити ім'я хосту: + Встановити ім'я хосту: - Set username and password - Встановити ім'я користувача і пароль + Встановити ім'я користувача і пароль - Username: - Ім'я користувача: + Ім'я користувача: - - Password: - Пароль: + Пароль: - Configure wireless LAN - Налаштувати бездротову LAN мережу + Налаштувати бездротову LAN мережу - SSID: - SSID: + SSID: - Show password - Показати пароль + Показати пароль - Hidden SSID - Прихована SSID + Прихована SSID - Wireless LAN country: - Країна бездротової LAN мережі: + Країна бездротової LAN мережі: - Set locale settings - Змінити налаштування регіону + Змінити налаштування регіону - Time zone: - Часова зона: + Часова зона: - Keyboard layout: - Розкладка клавіатури: + Розкладка клавіатури: - Enable SSH - Увімкнути SSH + Увімкнути SSH - Use password authentication - Використовувати аутентефікацію через пароль + Використовувати аутентефікацію через пароль - Allow public-key authentication only - Дозволити аутентифікацію лише через публічні ключі + Дозволити аутентифікацію лише через публічні ключі - Set authorized_keys for '%1': - Встановити authorized_keys для '%1': + Встановити authorized_keys для '%1': - RUN SSH-KEYGEN - ЗАПУСТИТИ SHH-KEYGEN + ЗАПУСТИТИ SHH-KEYGEN - Play sound when finished - Відтворити звук після завершення + Відтворити звук після завершення - Eject media when finished - Витягнути накопичувач після завершення + Витягнути накопичувач після завершення - Enable telemetry - Увімкнути телеметрію + Увімкнути телеметрію - + SAVE ЗБЕРЕГТИ @@ -433,10 +610,48 @@ Постійні налаштування + + OptionsServicesTab + + + Enable SSH + Увімкнути SSH + + + + Use password authentication + Використовувати аутентефікацію через пароль + + + + Allow public-key authentication only + Дозволити аутентифікацію лише через публічні ключі + + + + Set authorized_keys for '%1': + Встановити authorized_keys для '%1': + + + + Delete Key + + + + + RUN SSH-KEYGEN + ЗАПУСТИТИ SHH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader Внутрішній считувач SD карт @@ -444,32 +659,31 @@ UseSavedSettingsPopup - Use OS customization? - Використовувати налаштування ОС? + Використовувати налаштування ОС? - + Would you like to apply OS customization settings? Чи бажаєте ви прийняти налаштування ОС? - + NO НІ - + NO, CLEAR SETTINGS НІ, ОЧИСТИТИ НАЛАШТУВАННЯ - + YES ТАК - + EDIT SETTINGS РЕДАГУВАТИ НАЛАШТУВАННЯ @@ -477,62 +691,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager, версія %1 - - + Raspberry Pi Device Пристрій Raspberry Pi - CHOOSE DEVICE - ОБРАТИ ПРИСТРІЙ + ОБРАТИ ПРИСТРІЙ - + Select this button to choose your target Raspberry Pi Оберіть цю кнопку, щоб обрати модель вашої Raspberry Pi - - + Operating System Операційна система - - + + CHOOSE OS ОБРАТИ ОС - + Select this button to change the operating system Натисніть на цю кнопку, щоб змінити операційну систему - - + Storage Накопичувач - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE ОБРАТИ НАКОПИЧУВАЧ @@ -541,40 +746,40 @@ ЗАПИСАТИ - + Select this button to change the destination storage device Натисніть цю кнопку, щоб змінити пристрій призначення - + CANCEL WRITE СКАСУВАТИ ЗАПИСУВАННЯ - - + + Cancelling... Скасування... - + CANCEL VERIFY СКАСУВАТИ ПЕРЕВІРКУ - - - + + + Finalizing... Завершення... - + Next Далі - + Select this button to start writing the image Натисніть цю кнопку, щоб розпочати запис образу @@ -583,22 +788,22 @@ Натисніть цю кнопку, щоб отримати доступ до розширених опцій - + Using custom repository: %1 Користуючись власним репозиторієм: %1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists Навігація клавіатурою: клавіша <Tab> переміститися на наступну кнопку, клавіша <Пробіл> натиснути кнопку/обрати елемент, клавіши з <стрілками вниз/вгору> переміститися вниз/вгору по списку - + Language: Мова: - + Keyboard: Клавіатура: @@ -607,84 +812,73 @@ Модель Raspberry Pi: - [ All ] - [Усі] + [Усі] - Back - Назад + Назад - Go back to main menu - Повернутися у головне меню + Повернутися у головне меню - Released: %1 - Випущено: %1 + Випущено: %1 - Cached on your computer - Кешовано на вашому комп'ютері + Кешовано на вашому комп'ютері - Local file - Локальний файл + Локальний файл - Online - %1 GB download - Онлайн - потрібно завантажити %1 ГБ + Онлайн - потрібно завантажити %1 ГБ - - - Mounted as %1 - Примонтовано як %1 + Примонтовано як %1 - [WRITE PROTECTED] - ЗАХИЩЕНО ВІД ЗАПИСУ + ЗАХИЩЕНО ВІД ЗАПИСУ - + Are you sure you want to quit? Бажаєте вийти? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager все ще зайнятий.<br>Ви впевнені, що бажаєте вийти? - + Warning Увага - + Preparing to write... Підготовка до запису... - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? Усі уснуючі дані у '%1' будуть видалені.<br> Ви впевнені, що бажаєте продовжити? - + Update available Доступно оновлення - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? Доступна нова версія Imager.<br>Бажаєте завітати на сайт та завантажити її? @@ -693,75 +887,72 @@ Помилка завантаження списку ОС із Інтернету - + Writing... %1% Записування...%1% - + Verifying... %1% Перевірка...%1% - + Preparing to write... (%1) Підготовка до запису... (%1) - + Error Помилка - + Write Successful Успішно записано - - + + Erase Видалити - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> був успішно видалено.br><br> тепер можна дістати SD карту із считувача - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader Записування <b>%1</b> на <b>%2</b> виконано <br><br> Тепер можна дістати SD карту із считувача - Error parsing os_list.json - Помилка парсування os_list.json + Помилка парсування os_list.json - + Format card as FAT32 Форматувати карту у FAT32 - + Use custom Власний образ - + Select a custom .img from your computer Обрати власний .img з вашого комп'ютера - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - Спочатку під'єднайте USB-накопичувач з образами.<br>Образи повинні знаходитися у корінному каталогу USB-накопичувача. + Спочатку під'єднайте USB-накопичувач з образами.<br>Образи повинні знаходитися у корінному каталогу USB-накопичувача. - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD карта захищена від запису.<br>перетягніть перемикач на лівій стороні картки вгору, і спробуйте ще раз. + SD карта захищена від запису.<br>перетягніть перемикач на лівій стороні картки вгору, і спробуйте ще раз. diff --git a/src/i18n/rpi-imager_zh-TW.ts b/src/i18n/rpi-imager_zh-TW.ts index 6666dee8..33ce42bc 100644 --- a/src/i18n/rpi-imager_zh-TW.ts +++ b/src/i18n/rpi-imager_zh-TW.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 解壓縮檔案時發生錯誤:%1 - + Error mounting FAT32 partition 掛載 FAT32 分割區時發生錯誤 - + Operating system did not mount FAT32 partition 作業系統未掛載 FAT32 分割區 - + Error changing to directory '%1' 變更至目錄 '%1' 時發生錯誤 @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive 正在卸載磁碟 - + opening drive 正在開啟磁碟 - + Error running diskpart: %1 執行 diskpart 時發生錯誤:%1 - + Error removing existing partitions 移除現有分割區時發生錯誤 - + Authentication cancelled 已取消驗證 - + Error running authopen to gain access to disk device '%1' 執行 authopen 以獲取磁碟裝置 '%1' 存取權限時發生錯誤 - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). 請確認在隱私設定中是否允許 'Raspberry Pi Imager' 存取 '可移除磁碟'(在 '檔案和資料夾' 下,或者給予它 '完全磁碟存取權')。 - + Cannot open storage device '%1'. 無法開啟儲存裝置 '%1'。 - + discarding existing data on drive 正在丟棄磁碟上的現有資料 - + zeroing out first and last MB of drive 正在將磁碟的第一個和最後一個 MB 清除 - + Write error while zero'ing out MBR 在將 MBR 清除時寫入錯誤 - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). 嘗試將卡的最後部分清除時寫入錯誤。<br>卡可能在廣告錯誤的容量(可能是偽造的)。 - + starting download 開始下載 - + Error downloading: %1 下載時發生錯誤:%1 - + Access denied error while writing file to disk. 寫入檔案至磁碟時存取被拒絕的錯誤。 - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. 似乎已啟用受控資料夾存取。請將 rpi-imager.exe 和 fat32format.exe 兩者都加入允許的應用程式清單,然後再試一次。 - + Error writing file to disk 寫入檔案至磁碟時發生錯誤 - + Download corrupt. Hash does not match 下載損壞。雜湊值不符 - - + + Error writing to storage (while flushing) 寫入儲存裝置時發生錯誤(在清除時) - - + + Error writing to storage (while fsync) 寫入儲存裝置時發生錯誤(在 fsync 時) - + Error writing first block (partition table) 寫入第一個區塊(分割表)時發生錯誤 - + Error reading from storage.<br>SD card may be broken. 從儲存裝置讀取時發生錯誤。<br>SD 卡可能已損壞。 - + Verifying write failed. Contents of SD card is different from what was written to it. 驗證寫入失敗。SD 卡的內容與寫入的內容不一致。 - + Customizing image 自訂映像檔 @@ -210,40 +210,105 @@ 此平台未實作格式化 + + DstPopup + + + Storage + 儲存裝置 + + + + No storage devices found + + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + 已掛載為 %1 + + + + GB + + + + + [WRITE PROTECTED] + [寫入保護] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD 卡已啟用寫入保護。<br>請將卡片左側的鎖定開關向上推以解除保護,然後再試一次。 + + + + HWListModel + + + CHOOSE DEVICE + 選擇裝置 + + + + HwPopup + + + Raspberry Pi Device + Raspberry Pi 裝置 + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. 儲存容量不足。<br>至少需要 %1 GB。 - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. 輸入檔案不是有效的磁碟映像檔。<br>檔案大小 %1 位元組不是 512 位元組的倍數。 - + Downloading and writing image 正在下載並寫入映像檔 - + Select image 選擇映像檔 - + Error synchronizing time. Trying again in 3 seconds - + STP is enabled on your Ethernet switch. Getting IP will take long time. - + Would you like to prefill the wifi password from the system keychain? 您是否想要從系統鑰匙圈預填 Wi-Fi 密碼? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file 正在開啟映像檔 - + Error opening image file 開啟映像檔時發生錯誤 @@ -264,30 +329,163 @@ MsgPopup - + NO - + YES - + CONTINUE 繼續 - + QUIT 結束 + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + 作業系統 + + + + Back + 返回 + + + + Go back to main menu + 返回主選單 + + + + Released: %1 + 發布日期:%1 + + + + Cached on your computer + 已在您的電腦上快取 + + + + Local file + 本機檔案 + + + + Online - %1 GB download + 線上 - %1 GB 下載 + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + 請先插入包含映像檔的 USB 隨身碟。<br>映像檔必須位於 USB 隨身碟的根目錄中。 + + + + OptionsGeneralTab + + + Set hostname: + 設定主機名稱: + + + + Set username and password + 設定使用者名稱和密碼 + + + + Username: + 使用者名稱: + + + + + Password: + 密碼: + + + + Configure wireless LAN + 設定無線區域網路 + + + + SSID: + SSID: + + + + Show password + 顯示密碼 + + + + Hidden SSID + 隱藏 SSID + + + + Wireless LAN country: + 無線網路國家/地區選項: + + + + Set locale settings + 設定地區設定 + + + + Time zone: + 時區: + + + + Keyboard layout: + 鍵盤配置: + + + + OptionsMiscTab + + + Play sound when finished + 完成時播放聲音 + + + + Eject media when finished + 完成時彈出媒體 + + + + Enable telemetry + 啟用遙測 + + OptionsPopup - + OS Customization 作業系統客製化 @@ -304,131 +502,148 @@ 總是使用 - + General 一般 - + Services 服務 - + Options 選項 - Set hostname: - 設定主機名稱: + 設定主機名稱: - Set username and password - 設定使用者名稱和密碼 + 設定使用者名稱和密碼 - Username: - 使用者名稱: + 使用者名稱: - - Password: - 密碼: + 密碼: - Configure wireless LAN - 設定無線區域網路 + 設定無線區域網路 - SSID: - SSID: + SSID: - Show password - 顯示密碼 + 顯示密碼 - Hidden SSID - 隱藏 SSID + 隱藏 SSID - Wireless LAN country: - 無線網路國家/地區選項: + 無線網路國家/地區選項: - Set locale settings - 設定地區設定 + 設定地區設定 - Time zone: - 時區: + 時區: - Keyboard layout: - 鍵盤配置: + 鍵盤配置: - Enable SSH - 啟用 SSH + 啟用 SSH - Use password authentication - 使用密碼驗證 + 使用密碼驗證 - Allow public-key authentication only - 僅允許公鑰驗證 + 僅允許公鑰驗證 - Set authorized_keys for '%1': - 設定 '%1' 的 authorized_keys: + 設定 '%1' 的 authorized_keys: - RUN SSH-KEYGEN - 執行 SSH-KEYGEN + 執行 SSH-KEYGEN - Play sound when finished - 完成時播放聲音 + 完成時播放聲音 - Eject media when finished - 完成時彈出媒體 + 完成時彈出媒體 - Enable telemetry - 啟用遙測 + 啟用遙測 - + SAVE 儲存 + + OptionsServicesTab + + + Enable SSH + 啟用 SSH + + + + Use password authentication + 使用密碼驗證 + + + + Allow public-key authentication only + 僅允許公鑰驗證 + + + + Set authorized_keys for '%1': + 設定 '%1' 的 authorized_keys: + + + + Delete Key + + + + + RUN SSH-KEYGEN + 執行 SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader 內部 SD 卡讀卡機 @@ -436,32 +651,31 @@ UseSavedSettingsPopup - Use OS customization? - 作業系統客製化? + 作業系統客製化? - + Would you like to apply OS customization settings? 您是否想要套用作業系統客製化設定? - + NO - + NO, CLEAR SETTINGS 否,清除設定 - + YES - + EDIT SETTINGS 編輯設定 @@ -469,62 +683,53 @@ main - + Raspberry Pi Imager v%1 Raspberry Pi Imager v%1 - - + Raspberry Pi Device Raspberry Pi 裝置 - CHOOSE DEVICE - 選擇裝置 + 選擇裝置 - + Select this button to choose your target Raspberry Pi 選擇此按鈕以選擇您的目標 Raspberry Pi - - + Operating System 作業系統 - - + + CHOOSE OS 選擇作業系統 - + Select this button to change the operating system 選擇此按鈕以變更作業系統 - - + Storage 儲存裝置 - + Network not ready yet - - No storage devices found - - - - - + + CHOOSE STORAGE 選擇儲存裝置 @@ -533,40 +738,40 @@ 寫入 - + Select this button to change the destination storage device 選擇此按鈕以變更目標儲存裝置 - + CANCEL WRITE 取消寫入 - - + + Cancelling... 正在取消… - + CANCEL VERIFY 取消驗證 - - - + + + Finalizing... 正在完成… - + Next 下一步 - + Select this button to start writing the image 選擇此按鈕以開始寫入映像檔 @@ -575,22 +780,22 @@ 選擇此按鈕以存取進階設定 - + Using custom repository: %1 使用自訂套件庫:%1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists 鍵盤操作:<tab> 移至下一個按鈕 <space> 按下按鈕/選取項目 <arrow up/down> 在清單中上移/下移 - + Language: 語言: - + Keyboard: 鍵盤: @@ -599,84 +804,73 @@ Pi 型號: - [ All ] - [ 全部 ] + [ 全部 ] - Back - 返回 + 返回 - Go back to main menu - 返回主選單 + 返回主選單 - Released: %1 - 發布日期:%1 + 發布日期:%1 - Cached on your computer - 已在您的電腦上快取 + 已在您的電腦上快取 - Local file - 本機檔案 + 本機檔案 - Online - %1 GB download - 線上 - %1 GB 下載 + 線上 - %1 GB 下載 - - - Mounted as %1 - 已掛載為 %1 + 已掛載為 %1 - [WRITE PROTECTED] - [寫入保護] + [寫入保護] - + Are you sure you want to quit? 您確定要結束嗎? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? Raspberry Pi Imager 仍在忙碌中。<br>您確定要結束嗎? - + Warning 警告 - + Preparing to write... 正在準備寫入… - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? '%1' 上的所有現有資料將被清除。<br>您確定要繼續嗎? - + Update available 有可用的更新 - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? 有新版本的 Imager 可供下載。<br>您是否想前往網站進行下載? @@ -685,75 +879,72 @@ 從網路下載作業系統清單時發生錯誤 - + Writing... %1% 正在寫入… %1% - + Verifying... %1% 正在驗證… %1% - + Preparing to write... (%1) 正在準備寫入… (%1) - + Error 錯誤 - + Write Successful 寫入成功 - - + + Erase 清除 - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1</b> 已被清除<br><br>您現在可以從讀卡機中移除 SD 卡 - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> 已寫入至 <b>%2</b><br><br>您現在可以從讀卡機中移除 SD 卡 - Error parsing os_list.json - 解析 os_list.json 時發生錯誤 + 解析 os_list.json 時發生錯誤 - + Format card as FAT32 將記憶卡進行 FAT32 格式化 - + Use custom 使用自訂映像檔 - + Select a custom .img from your computer 從您的電腦選擇一個自訂的 .img 檔案 - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - 請先插入包含映像檔的 USB 隨身碟。<br>映像檔必須位於 USB 隨身碟的根目錄中。 + 請先插入包含映像檔的 USB 隨身碟。<br>映像檔必須位於 USB 隨身碟的根目錄中。 - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD 卡已啟用寫入保護。<br>請將卡片左側的鎖定開關向上推以解除保護,然後再試一次。 + SD 卡已啟用寫入保護。<br>請將卡片左側的鎖定開關向上推以解除保護,然後再試一次。 diff --git a/src/i18n/rpi-imager_zh.ts b/src/i18n/rpi-imager_zh.ts index 8e12aa91..59be7479 100644 --- a/src/i18n/rpi-imager_zh.ts +++ b/src/i18n/rpi-imager_zh.ts @@ -4,23 +4,23 @@ DownloadExtractThread - - + + Error extracting archive: %1 解压至 %1 时发生错误 - + Error mounting FAT32 partition 挂载 FAT32 分区时发生错误 - + Operating system did not mount FAT32 partition 操作系统未能挂载 FAT32 分区 - + Error changing to directory '%1' 进入文件夹 “%1” 时发生错误 @@ -28,124 +28,124 @@ DownloadThread - + unmounting drive 卸载存储设备 - + opening drive 打开存储设备 - + Error running diskpart: %1 运行 “diskpart” 命令时发生错误:%1 - + Error removing existing partitions 删除现有分区时发生错误 - + Authentication cancelled 验证已取消 - + Error running authopen to gain access to disk device '%1' 在运行 authopen 以获取对磁盘设备 “%1” 的访问权限时发生错误 - + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). 请检查,在隐私设置中是否允许树莓派启动盘制作工具(Raspberry Pi Imager)访问“可移除的宗卷”(位于“文件和文件夹”下),或为其授予“完全磁盘访问权限”。 - + Cannot open storage device '%1'. 无法打开存储设备 “%1”。 - + discarding existing data on drive 清除存储设备上的已有数据 - + zeroing out first and last MB of drive 将存储设备的首尾 1 M 清零 - + Write error while zero'ing out MBR 清零 MBR 时写入错误 - + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). 在对设备尾部清零时写入错误<br>SD 卡标称的容量错误(可能是扩容卡)。 - + starting download 开始下载 - + Error downloading: %1 下载错误,已下载:%1 - + Access denied error while writing file to disk. 将文件写入磁盘时被拒绝访问。 - + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. 似乎已启用受控文件夹访问权限。 请将 rpi-imager.exe 和 fat32format.exe 添加至允许的应用程序列表中,然后重试。 - + Error writing file to disk 将文件写入磁盘时发生错误 - + Download corrupt. Hash does not match 下载的文件已损坏。校验值不匹配 - - + + Error writing to storage (while flushing) 写入存储设备时发生错误 - - + + Error writing to storage (while fsync) 在写入存储设备时(fsync)发生错误 - + Error writing first block (partition table) 在写入第一个区块(分区表)时发生错误 - + Error reading from storage.<br>SD card may be broken. 读取存储设备时发生错误。<br>SD 卡可能已损坏。 - + Verifying write failed. Contents of SD card is different from what was written to it. 写入校验失败。SD 卡与写入的内容不一致。 - + Customizing image 使用自定义镜像 @@ -210,40 +210,105 @@ 暂不支持在该平台上进行格式化 + + DstPopup + + + Storage + 储存设备 + + + + No storage devices found + 找不到存储设备 + + + + Exclude System Drives + + + + + gigabytes + + + + + + Mounted as %1 + 已挂载为:%1 + + + + GB + + + + + [WRITE PROTECTED] + [写保护] + + + + SYSTEM + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD 卡写保护。<br>请尝试向上推动 SD 卡左侧的锁定开关,然后再试一次。 + + + + HWListModel + + + CHOOSE DEVICE + 选择设备 + + + + HwPopup + + + Raspberry Pi Device + 树莓派设备 + + ImageWriter - + Storage capacity is not large enough.<br>Needs to be at least %1 GB. 存储容量不足。<br>至少需要 %1 GB 的剩余空间. - + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. 所选文件是无效的磁盘镜像。<br>文件大小 %1 B 不是 512 B 的整倍数。 - + Downloading and writing image 下载和写入镜像 - + Select image 选择镜像 - + Error synchronizing time. Trying again in 3 seconds 时间同步错误。将在 3 秒后重试 - + STP is enabled on your Ethernet switch. Getting IP will take long time. 您的以太网交换机启用了 STP。获取 IP 地址可能需要较长时间。 - + Would you like to prefill the wifi password from the system keychain? 您想要通过系统钥匙串访问自动填充 WiFi 密码吗? @@ -251,12 +316,12 @@ LocalFileExtractThread - + opening image file 打开镜像文件 - + Error opening image file 读取镜像文件时发生错误 @@ -264,159 +329,309 @@ MsgPopup - + NO - + YES 确认 - + CONTINUE 继续 - + QUIT 退出 + + OSListModel + + + Recommended + + + + + OSPopup + + + Operating System + 操作系统 + + + + Back + 返回 + + + + Go back to main menu + 回到主菜单 + + + + Released: %1 + 发布时间:%1 + + + + Cached on your computer + 已缓存到本地磁盘 + + + + Local file + 本地文件 + + + + Online - %1 GB download + 需要下载:%1 GB + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + 请先接入一款包含镜像的 USB 设备。<br>其镜像必须位于该设备的根路径下。 + + + + OptionsGeneralTab + + + Set hostname: + 设置主机名: + + + + Set username and password + 设置用户名及密码 + + + + Username: + 用户名: + + + + + Password: + 密码: + + + + Configure wireless LAN + 配置 WLAN + + + + SSID: + 网络名称: + + + + Show password + 显示密码 + + + + Hidden SSID + 隐藏的网络 + + + + Wireless LAN country: + WLAN 区域: + + + + Set locale settings + 本地化设置 + + + + Time zone: + 时区: + + + + Keyboard layout: + 键盘布局: + + + + OptionsMiscTab + + + Play sound when finished + 在完成后播放提示音 + + + + Eject media when finished + 在完成后卸载磁盘 + + + + Enable telemetry + 启用遥测 + + OptionsPopup - + OS Customization 自定义系统配置 - + General 通用 - + Services 服务 - + Options 可选配置 - Set hostname: - 设置主机名: + 设置主机名: - Set username and password - 设置用户名及密码 + 设置用户名及密码 - Username: - 用户名: + 用户名: - - Password: - 密码: + 密码: - Configure wireless LAN - 配置 WLAN + 配置 WLAN - SSID: - 网络名称: + 网络名称: - Show password - 显示密码 + 显示密码 - Hidden SSID - 隐藏的网络 + 隐藏的网络 - Wireless LAN country: - WLAN 区域: + WLAN 区域: - Set locale settings - 本地化设置 + 本地化设置 - Time zone: - 时区: + 时区: - Keyboard layout: - 键盘布局: + 键盘布局: - Enable SSH - 开启 SSH + 开启 SSH - Use password authentication - 使用密码登录 + 使用密码登录 - Allow public-key authentication only - 仅使用公钥登录 + 仅使用公钥登录 - Set authorized_keys for '%1': - 为用户 “%1” 设置 authorized_keys: + 为用户 “%1” 设置 authorized_keys: - RUN SSH-KEYGEN - 运行 SSH-KEYGEN + 运行 SSH-KEYGEN - Play sound when finished - 在完成后播放提示音 + 在完成后播放提示音 - Eject media when finished - 在完成后卸载磁盘 + 在完成后卸载磁盘 - Enable telemetry - 启用遥测 + 启用遥测 - + SAVE 保存 + + OptionsServicesTab + + + Enable SSH + 开启 SSH + + + + Use password authentication + 使用密码登录 + + + + Allow public-key authentication only + 仅使用公钥登录 + + + + Set authorized_keys for '%1': + 为用户 “%1” 设置 authorized_keys: + + + + Delete Key + + + + + RUN SSH-KEYGEN + 运行 SSH-KEYGEN + + + + Add SSH Key + + + QObject - + Internal SD card reader 内置 SD 卡读卡器 @@ -424,32 +639,31 @@ UseSavedSettingsPopup - Use OS customization? - 使用自定义系统配置? + 使用自定义系统配置? - + Would you like to apply OS customization settings? 您想应用自定义系统配置吗? - + NO - + NO, CLEAR SETTINGS 否,并清空所有设置 - + YES 确认 - + EDIT SETTINGS 编辑设置 @@ -457,275 +671,256 @@ main - + Raspberry Pi Imager v%1 树莓派启动盘制作工具 v%1 - - + Raspberry Pi Device 树莓派设备 - CHOOSE DEVICE - 选择设备 + 选择设备 - + Select this button to choose your target Raspberry Pi 点击此处选择您的树莓派型号 - - + Operating System 操作系统 - - + + CHOOSE OS 选择操作系统 - + Select this button to change the operating system 点击此处更改操作系统 - - + Storage 储存设备 - + Network not ready yet 网络不可用 - No storage devices found - 找不到存储设备 + 找不到存储设备 - - + + CHOOSE STORAGE 选择储存设备 - + Select this button to change the destination storage device 点击此处更改所选存储设备 - + CANCEL WRITE 取消写入 - - + + Cancelling... 正在取消…… - + CANCEL VERIFY 取消校验 - - - + + + Finalizing... 正在完成... - + Next 下一步 - + Select this button to start writing the image 点击此处开始写入 - + Using custom repository: %1 使用自定义存储库(repository):%1 - + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists 键盘导航:<tab> 切换选项 <space> 确认/选择后使用 <arrow up/down> 可在列表中上下移动 - + Language: 语言: - + Keyboard: 键盘: - [ All ] - [ All ] + [ All ] - Back - 返回 + 返回 - Go back to main menu - 回到主菜单 + 回到主菜单 - Released: %1 - 发布时间:%1 + 发布时间:%1 - Cached on your computer - 已缓存到本地磁盘 + 已缓存到本地磁盘 - Local file - 本地文件 + 本地文件 - Online - %1 GB download - 需要下载:%1 GB + 需要下载:%1 GB - - - Mounted as %1 - 已挂载为:%1 + 已挂载为:%1 - [WRITE PROTECTED] - [写保护] + [写保护] - + Are you sure you want to quit? 您确定要退出吗? - + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? 树莓派启动盘制作工具还未完成任务。<br>您确定要退出吗? - + Warning 警告 - + Preparing to write... 准备写入…… - + All existing data on '%1' will be erased.<br>Are you sure you want to continue? '%1' 上的已有数据都将被删除。<br>是否继续? - + Update available 检测到更新 - + There is a newer version of Imager available.<br>Would you like to visit the website to download it? 检测到新版树莓派启动盘制作工具。<br> 是否转到网页进行下载? - + Writing... %1% 正在写入…… %1% - + Verifying... %1% 正在校验…… %1% - + Preparing to write... (%1) 准备写入…… (%1) - + Error 错误 - + Write Successful 写入成功 - - + + Erase 格式化 - + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader <b>%1 </ b> 已成功格式化<br> <br>您现在可以从读卡器上取下 SD 卡了 - + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader <b>%1</b> 已成功写入至 <b>%2</b><br><br>,您现在可以从读卡器上取下 SD 卡了 - Error parsing os_list.json - os_list.json 解析错误 + os_list.json 解析错误 - + Format card as FAT32 把 SD 卡格式化为 FAT32 - + Use custom 使用自定义镜像 - + Select a custom .img from your computer 选择本地已有的 .img 文件 - Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. - 请先接入一款包含镜像的 USB 设备。<br>其镜像必须位于该设备的根路径下。 + 请先接入一款包含镜像的 USB 设备。<br>其镜像必须位于该设备的根路径下。 - SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. - SD 卡写保护。<br>请尝试向上推动 SD 卡左侧的锁定开关,然后再试一次。 + SD 卡写保护。<br>请尝试向上推动 SD 卡左侧的锁定开关,然后再试一次。 From 30ef2f1edb6589b51bb98d9b8dda3e0e6d40dfbd Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Tue, 25 Feb 2025 14:36:13 +0000 Subject: [PATCH 44/47] Remove dummy comment leftover --- src/main.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.qml b/src/main.qml index 239561ec..e21cf054 100644 --- a/src/main.qml +++ b/src/main.qml @@ -171,7 +171,6 @@ ApplicationWindow { padding: 0 bottomPadding: 0 topPadding: 0 - // text: window.imageWriter.getOSList().currentName TODO Layout.minimumHeight: 40 Layout.fillWidth: true onClicked: { From 4a31bfe066643d595ca00d143da9f2e99fb4fa91 Mon Sep 17 00:00:00 2001 From: Tom Dewey Date: Wed, 26 Feb 2025 16:22:05 +0000 Subject: [PATCH 45/47] cmake: Disallow homebrew on macOS This will only work for CMake modules that obey [SYSTEM]_IGNORE_PATH, but it's better than nothing. --- src/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6416d688..7cb61349 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,15 @@ if (APPLE) set(IMAGER_SIGNING_IDENTITY "" CACHE STRING "The Developer Identity to use for signing.") set(IMAGER_NOTARIZE_APP OFF CACHE BOOL "Perform notarization of the Imager .dmg as part of the build") set(IMAGER_NOTARIZE_KEYCHAIN_PROFILE "" CACHE STRING "The name of the Keychain item containing your notarization credentials") + + # Per CMake Bug 21918, if you do not use the following block, CMake will automatically include homebrew libraries. + # This is undesirable, as on macOS/Apple Silicon, you may find newer versions of Qt will pull in new dependencies + # - breaking your x86_64 build prematurely. Regardless, this is desirable behaviour to make the build more predictable. + set(CMAKE_IGNORE_PATH) + foreach(_prefix /sw /opt/local /usr/local /opt/homebrew) + list(APPEND CMAKE_IGNORE_PATH ${_prefix}/bin ${_prefix}/include ${_prefix}/lib) + list(APPEND CMAKE_SYSTEM_IGNORE_PATH ${_prefix}/bin ${_prefix}/include ${_prefix}/lib) + endforeach() endif(APPLE) ## Preferentially build the bundled code. Full vendoring is to follow in a later version. From 7fc6d2487c9d0fa726367b1aee6dc2ec94a9b917 Mon Sep 17 00:00:00 2001 From: Tom Dewey Date: Wed, 26 Feb 2025 16:23:26 +0000 Subject: [PATCH 46/47] build: Add nghttp2 via FetchContent As we don't need to modify nghttp2, use the FetchContent mechanism to obtain the code for nghttp2. Note that this isn't a voilation of my vendoring policy - the policy is designed to ensure we know what version is being built - not that it can be built entirely offline at all times. --- src/CMakeLists.txt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7cb61349..b796b1b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,8 +43,6 @@ if (APPLE) endforeach() endif(APPLE) -## Preferentially build the bundled code. Full vendoring is to follow in a later version. - # Bundled code will occasionally use identical options - eg, BUILD_TESTING. set(BUILD_TESTING OFF) set(BUILD_STATIC_LIBS ON) @@ -90,6 +88,20 @@ set(Zstd_LIBRARIES libzstd_static) set(ZSTD_LIBRARIES libzstd_static) set(ZSTD_LIBRARY libzstd_static) +# Remote nghttp2 +include(FetchContent) +FetchContent_Declare(nghttp2 + GIT_REPOSITORY https://github.com/nghttp2/nghttp2.git + GIT_TAG v1.64.0 +) +set(BUILD_EXAMPLES OFF) +set(ENABLE_LIB_ONLY ON) +set(ENABLE_FAILMALLOC OFF) +FetchContent_MakeAvailable(nghttp2) +unset(ENABLE_LIB_ONLY) +unset(ENABLE_FAILMALLOC) +unset(BUILD_EXAMPLES) + # Bundled zlib set(ZLIB_BUILD_EXAMPLES OFF) set(SKIP_INSTALL_ALL ON) From 609e2fd5f4a600c7ef849a4983742f7967e20305 Mon Sep 17 00:00:00 2001 From: Tom Dewey Date: Wed, 26 Feb 2025 16:24:40 +0000 Subject: [PATCH 47/47] cmake: macos: Fixup unsigned builds A missing move command in the entirely-unsigned path would result in a CMake failure. Note that this isn't the most useful target - you can't run an unsigned application on Apple Silicon, but you _can_ build it to confirm at least that stage works. --- src/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b796b1b2..f0996468 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -548,6 +548,9 @@ elseif(APPLE) endif(IMAGER_SIGNING_IDENTITY) else() # Unsigned application + add_custom_command(TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND mv "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app" "${CMAKE_BINARY_DIR}/Raspberry\ Pi\ Imager.app") add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${CMAKE_BINARY_DIR}/Raspberry\ Pi\ Imager.app" -qmldir="${CMAKE_CURRENT_SOURCE_DIR}" -always-overwrite -no-strip -dmg)