2020-03-04 16:55:40 +01:00
/ *
* SPDX - License - Identifier: Apache - 2.0
* Copyright ( C ) 2020 Raspberry Pi ( Trading ) Limited
* /
import QtQuick 2.9
import QtQuick . Window 2.2
import QtQuick . Controls 2.2
import QtQuick . Layouts 1.0
import QtQuick . Controls . Material 2.2
ApplicationWindow {
id: window
visible: true
2020-06-01 17:45:41 +02:00
width: imageWriter . isEmbeddedMode ( ) ? - 1 : 680
height: imageWriter . isEmbeddedMode ( ) ? - 1 : 420
minimumWidth: imageWriter . isEmbeddedMode ( ) ? - 1 : 680
2021-03-26 15:55:54 +01:00
//maximumWidth: imageWriter.isEmbeddedMode() ? -1 : 680
2020-06-01 17:45:41 +02:00
minimumHeight: imageWriter . isEmbeddedMode ( ) ? - 1 : 420
2021-03-26 15:55:54 +01:00
//maximumHeight: imageWriter.isEmbeddedMode() ? -1 : 420
2020-03-04 16:55:40 +01:00
2020-03-10 17:43:48 +01:00
title: qsTr ( "Raspberry Pi Imager v%1" ) . arg ( imageWriter . constantVersion ( ) )
2020-03-04 16:55:40 +01:00
FontLoader { id: roboto ; source: "fonts/Roboto-Regular.ttf" }
FontLoader { id: robotoLight ; source: "fonts/Roboto-Light.ttf" }
FontLoader { id: robotoBold ; source: "fonts/Roboto-Bold.ttf" }
2020-05-23 19:07:03 +02:00
onClosing: {
if ( progressBar . visible ) {
close . accepted = false
2020-06-30 00:42:39 +02:00
quitpopup . openPopup ( )
2020-05-23 19:07:03 +02:00
}
}
2020-05-25 00:36:16 +02:00
Shortcut {
sequence: StandardKey . Quit
context: Qt . ApplicationShortcut
onActivated: {
if ( ! progressBar . visible ) {
Qt . quit ( )
}
}
}
2021-01-17 17:43:17 +01:00
Shortcut {
sequences: [ "Shift+Ctrl+X" , "Shift+Meta+X" ]
context: Qt . ApplicationShortcut
onActivated: {
optionspopup . openPopup ( )
}
}
2020-03-04 16:55:40 +01:00
ColumnLayout {
id: bg
spacing: 0
2020-06-01 17:45:41 +02:00
Rectangle {
implicitHeight: window . height / 2
Image {
id: image
Layout.fillWidth: true
Layout.alignment: Qt . AlignHCenter | Qt . AlignVCenter
fillMode: Image . PreserveAspectFit
2021-03-26 15:55:54 +01:00
source: window . height > 700 ? "icons/rpi2-hires.png" : "icons/rpi2.png"
2020-06-01 17:45:41 +02:00
width: window . width
height: window . height / 2
}
2020-03-04 16:55:40 +01:00
}
Rectangle {
color: "#c31c4a"
implicitWidth: window . width
implicitHeight: window . height / 2
GridLayout {
id: gridLayout
rowSpacing: 25
anchors.fill: parent
anchors.topMargin: 25
anchors.rightMargin: 50
anchors.leftMargin: 50
2021-11-23 12:09:03 +01:00
rows: 6
2020-03-04 16:55:40 +01:00
columns: 3
columnSpacing: 25
ColumnLayout {
id: columnLayout
spacing: 0
Layout.fillWidth: true
Text {
id: text1
color: "#ffffff"
text: qsTr ( "Operating System" )
Layout.fillWidth: true
Layout.preferredHeight: 17
Layout.preferredWidth: 100
font.pixelSize: 12
font.family: robotoBold . name
font.bold: true
horizontalAlignment: Text . AlignHCenter
}
Button {
id: osbutton
text: imageWriter . srcFileName ( ) === "" ? qsTr ( "CHOOSE OS" ) : imageWriter . srcFileName ( )
font.family: roboto . name
spacing: 0
padding: 0
bottomPadding: 0
topPadding: 0
Layout.minimumHeight: 40
Layout.fillWidth: true
2020-06-30 00:42:39 +02:00
onClicked: {
ospopup . open ( )
2020-12-01 15:52:33 +01:00
osswipeview . currentItem . forceActiveFocus ( )
2020-06-30 00:42:39 +02:00
}
2020-03-04 16:55:40 +01:00
Material.background: "#ffffff"
Material.foreground: "#c51a4a"
2020-06-30 00:42:39 +02:00
Accessible.ignored: ospopup . visible || dstpopup . visible
Accessible.description: qsTr ( "Select this button to change the operating system" )
Accessible.onPressAction: clicked ( )
2020-03-04 16:55:40 +01:00
}
}
ColumnLayout {
id: columnLayout2
spacing: 0
Layout.fillWidth: true
Text {
id: text2
color: "#ffffff"
2021-02-08 22:06:39 +01:00
text: qsTr ( "Storage" )
2020-03-04 16:55:40 +01:00
Layout.fillWidth: true
Layout.preferredHeight: 17
Layout.preferredWidth: 100
font.pixelSize: 12
font.family: robotoBold . name
font.bold: true
horizontalAlignment: Text . AlignHCenter
}
Button {
id: dstbutton
2021-02-08 22:06:39 +01:00
text: qsTr ( "CHOOSE STORAGE" )
2020-03-04 16:55:40 +01:00
font.family: roboto . name
Layout.minimumHeight: 40
Layout.preferredWidth: 100
Layout.fillWidth: true
onClicked: {
2020-07-02 23:31:20 +02:00
imageWriter . startDriveListPolling ( )
2020-03-04 16:55:40 +01:00
dstpopup . open ( )
2020-06-30 00:42:39 +02:00
dstlist . forceActiveFocus ( )
2020-03-04 16:55:40 +01:00
}
Material.background: "#ffffff"
Material.foreground: "#c51a4a"
2020-06-30 00:42:39 +02:00
Accessible.ignored: ospopup . visible || dstpopup . visible
2021-02-26 14:51:09 +01:00
Accessible.description: qsTr ( "Select this button to change the destination storage device" )
2020-06-30 00:42:39 +02:00
Accessible.onPressAction: clicked ( )
2020-03-04 16:55:40 +01:00
}
}
ColumnLayout {
spacing: 0
Layout.fillWidth: true
Text {
text: " "
Layout.preferredHeight: 17
Layout.preferredWidth: 100
}
Button {
id: writebutton
text: qsTr ( "WRITE" )
font.family: roboto . name
Layout.minimumHeight: 40
Layout.fillWidth: true
2020-06-30 00:42:39 +02:00
Accessible.ignored: ospopup . visible || dstpopup . visible
Accessible.description: qsTr ( "Select this button to start writing the image" )
2020-03-04 16:55:40 +01:00
enabled: false
Material.background: "#ffffff"
Material.foreground: "#c51a4a"
onClicked: {
2021-01-20 13:04:18 +01:00
if ( ! imageWriter . readyToWrite ( ) ) {
return
}
2020-06-30 00:42:39 +02:00
2021-11-18 20:48:24 +01:00
if ( ! optionspopup . initialized && imageWriter . imageSupportsCustomization ( ) && imageWriter . hasSavedCustomizationSettings ( ) ) {
2021-01-20 13:04:18 +01:00
usesavedsettingspopup . openPopup ( )
} else {
confirmwritepopup . askForConfirmation ( )
}
2020-03-04 16:55:40 +01:00
}
2020-06-30 00:42:39 +02:00
Accessible.onPressAction: clicked ( )
2020-03-04 16:55:40 +01:00
}
}
ColumnLayout {
id: columnLayout3
Layout.columnSpan: 3
2021-11-18 20:48:24 +01:00
Layout.alignment: Qt . AlignRight | Qt . AlignVCenter
2020-03-04 16:55:40 +01:00
Text {
id: progressText
font.pointSize: 10
color: "white"
font.family: robotoBold . name
font.bold: true
visible: false
horizontalAlignment: Text . AlignHCenter
Layout.fillWidth: true
}
ProgressBar {
id: progressBar
Layout.fillWidth: true
visible: false
Material.background: "#d15d7d"
}
Button {
id: cancelwritebutton
text: qsTr ( "CANCEL WRITE" )
onClicked: {
enabled = false
progressText . text = qsTr ( "Cancelling..." )
imageWriter . cancelWrite ( )
}
Material.background: "#ffffff"
Material.foreground: "#c51a4a"
Layout.alignment: Qt . AlignRight
visible: false
font.family: roboto . name
2020-06-30 00:42:39 +02:00
Accessible.onPressAction: clicked ( )
2020-03-04 16:55:40 +01:00
}
Button {
id: cancelverifybutton
text: qsTr ( "CANCEL VERIFY" )
onClicked: {
enabled = false
progressText . text = qsTr ( "Finalizing..." )
imageWriter . setVerifyEnabled ( false )
}
Material.background: "#ffffff"
Material.foreground: "#c51a4a"
Layout.alignment: Qt . AlignRight
visible: false
font.family: roboto . name
2020-06-30 00:42:39 +02:00
Accessible.onPressAction: clicked ( )
2020-03-04 16:55:40 +01:00
}
2021-11-18 20:48:24 +01:00
Image {
id: customizebutton
source: "icons/ic_cog_40px.svg"
visible: false
2021-11-22 00:21:30 +01:00
2021-11-18 20:48:24 +01:00
MouseArea {
anchors.fill: parent
onClicked: {
optionspopup . openPopup ( )
}
}
}
2020-03-04 16:55:40 +01:00
}
2021-11-22 00:21:30 +01:00
2021-11-23 12:09:03 +01:00
Text {
Layout.columnSpan: 3
color: "#ffffff"
font.pixelSize: 18
font.family: roboto . name
visible: imageWriter . isEmbeddedMode ( ) && imageWriter . customRepo ( )
text: qsTr ( "Using custom repository: %1" ) . arg ( imageWriter . constantOsListUrl ( ) )
}
Text {
Layout.columnSpan: 3
color: "#ffffff"
font.pixelSize: 18
font.family: roboto . name
visible: ! imageWriter . hasMouse ( )
text: qsTr ( "Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists" )
}
2021-11-22 00:21:30 +01:00
RowLayout {
id: langbar
Layout.columnSpan: 3
Layout.alignment: Qt . AlignCenter | Qt . AlignBottom
/ * F I X M E : s h o u l d n ' t u s e a n c h o r s h e r e . B u t L a y o u t b o t t o m a l i g n m e n t d o e s n o t
seem to be respected * /
anchors.bottom: parent . bottom
anchors.bottomMargin: 5
spacing: 10
visible: imageWriter . isEmbeddedMode ( )
Rectangle {
anchors.fill: langbar
color: "#ffffe3"
radius: 5
}
Text {
font.pixelSize: 12
font.family: roboto . name
text: qsTr ( "Language: " )
Layout.leftMargin: 30
Layout.topMargin: 10
Layout.bottomMargin: 10
}
ComboBox {
font.pixelSize: 12
font.family: roboto . name
model: imageWriter . getTranslations ( )
Layout.preferredWidth: 200
currentIndex: - 1
Component.onCompleted: {
currentIndex = find ( imageWriter . getCurrentLanguage ( ) )
}
onActivated: {
imageWriter . changeLanguage ( editText )
}
Layout.topMargin: 10
Layout.bottomMargin: 10
}
Text {
font.pixelSize: 12
font.family: roboto . name
text: qsTr ( "Keyboard: " )
Layout.topMargin: 10
Layout.bottomMargin: 10
}
ComboBox {
enabled: imageWriter . isEmbeddedMode ( )
font.pixelSize: 12
font.family: roboto . name
model: imageWriter . getKeymapLayoutList ( )
currentIndex: - 1
Component.onCompleted: {
currentIndex = find ( imageWriter . getCurrentKeyboard ( ) )
}
onActivated: {
imageWriter . changeKeyboard ( editText )
}
Layout.topMargin: 10
Layout.bottomMargin: 10
Layout.rightMargin: 30
}
}
/ * L a n g u a g e / k e y b o a r d b a r i s n o r m a l l y o n l y v i s i b l e i n e m b e d d e d m o d e .
To test translations also show it when shift + ctrl + L is pressed . * /
Shortcut {
sequences: [ "Shift+Ctrl+L" , "Shift+Meta+L" ]
context: Qt . ApplicationShortcut
onActivated: {
langbar . visible = true
}
}
2020-03-04 16:55:40 +01:00
}
}
}
/ *
Popup for OS selection
* /
Popup {
id: ospopup
x: 50
y: 25
width: parent . width - 100
height: parent . height - 50
padding: 0
closePolicy: Popup . CloseOnEscape | Popup . CloseOnPressOutside
2020-11-26 22:26:15 +01:00
property string categorySelected : ""
2020-03-04 16:55:40 +01:00
// background of title
Rectangle {
color: "#f5f5f5"
anchors.right: parent . right
anchors.top: parent . top
height: 35
width: parent . width
}
// line under title
Rectangle {
color: "#afafaf"
width: parent . width
y: 35
implicitHeight: 1
}
Text {
text: "X"
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: {
ospopup . close ( )
}
}
}
ColumnLayout {
spacing: 10
Text {
text: qsTr ( "Operating System" )
horizontalAlignment: Text . AlignHCenter
verticalAlignment: Text . AlignVCenter
Layout.fillWidth: true
Layout.topMargin: 10
font.family: roboto . name
font.bold: true
}
Item {
clip: true
2021-03-26 15:55:54 +01:00
Layout.preferredWidth: oslist . width
Layout.preferredHeight: oslist . height
2020-03-04 16:55:40 +01:00
SwipeView {
id: osswipeview
interactive: false
ListView {
id: oslist
model: osmodel
2020-06-30 00:42:39 +02:00
currentIndex: - 1
2020-03-04 16:55:40 +01:00
delegate: osdelegate
width: window . width - 100
height: window . height - 100
boundsBehavior: Flickable . StopAtBounds
2020-06-30 00:42:39 +02:00
highlight: Rectangle { color: "lightsteelblue" ; radius: 5 }
2020-03-04 16:55:40 +01:00
ScrollBar.vertical: ScrollBar {
width: 10
2020-05-23 13:02:38 +02:00
policy: oslist . contentHeight > oslist . height ? ScrollBar.AlwaysOn : ScrollBar . AsNeeded
2020-03-04 16:55:40 +01:00
}
2020-06-30 00:42:39 +02:00
Keys.onSpacePressed: {
if ( currentIndex != - 1 )
selectOSitem ( model . get ( currentIndex ) , true )
}
Accessible.onPressAction: {
if ( currentIndex != - 1 )
selectOSitem ( model . get ( currentIndex ) , true )
}
2020-03-04 16:55:40 +01:00
}
2020-12-01 15:52:33 +01:00
}
}
}
}
2020-03-04 16:55:40 +01:00
2020-12-01 15:52:33 +01:00
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: [ ]
name: qsTr ( "Back" )
description: qsTr ( "Go back to main menu" )
tooltip: ""
2021-05-07 13:10:23 +02:00
website: ""
2021-11-18 20:48:24 +01:00
init_format: ""
2020-03-04 16:55:40 +01:00
}
}
2020-12-01 15:52:33 +01:00
currentIndex: - 1
delegate: osdelegate
width: window . width - 100
height: window . height - 100
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 ) )
}
2020-03-04 16:55:40 +01:00
}
}
ListModel {
id: osmodel
ListElement {
url: "internal://format"
2020-12-09 14:54:54 +01:00
icon: "icons/erase.png"
2020-03-04 16:55:40 +01:00
extract_size: 0
image_download_size: 0
extract_sha256: ""
contains_multiple_files: false
release_date: ""
subitems_url: ""
subitems: [ ]
name: qsTr ( "Erase" )
description: qsTr ( "Format card as FAT32" )
2020-05-23 12:30:06 +02:00
tooltip: ""
2021-05-07 13:10:23 +02:00
website: ""
2021-11-18 20:48:24 +01:00
init_format: ""
2020-03-04 16:55:40 +01:00
}
ListElement {
url: ""
2020-12-09 14:54:54 +01:00
icon: "icons/use_custom.png"
2020-03-04 16:55:40 +01:00
name: qsTr ( "Use custom" )
description: qsTr ( "Select a custom .img from your computer" )
}
Component.onCompleted: {
2020-05-25 00:36:16 +02:00
if ( imageWriter . isOnline ( ) ) {
fetchOSlist ( ) ;
}
2020-03-04 16:55:40 +01:00
}
}
Component {
id: osdelegate
Item {
width: window . width - 100
height: image_download_size ? 100 : 60
2020-06-30 00:42:39 +02:00
Accessible.name: name + ".\n" + description
2020-03-04 16:55:40 +01:00
Rectangle {
id: bgrect
anchors.fill: parent
color: "#f5f5f5"
2020-06-30 00:42:39 +02:00
visible: mouseOver && parent . ListView . view . currentIndex !== index
property bool mouseOver: false
2020-03-04 16:55:40 +01:00
}
Rectangle {
id: borderrect
implicitHeight: 1
implicitWidth: parent . width
color: "#dcdcdc"
y: parent . height
}
Row {
leftPadding: 25
Column {
width: 64
Image {
2020-12-09 14:54:54 +01:00
source: icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png" : icon
2020-03-04 16:55:40 +01:00
verticalAlignment: Image . AlignVCenter
height: parent . parent . parent . height
fillMode: Image . Pad
}
Text {
text: " "
// visible: !icon
}
}
Column {
width: parent . parent . width - 64 - 50 - 25
Text {
verticalAlignment: Text . AlignVCenter
height: parent . parent . parent . height
font.family: roboto . name
textFormat: Text . RichText
text: {
2021-05-07 13:10:23 +02:00
var txt = "<p style='margin-bottom: 5px; font-weight: bold;'>" + name
if ( typeof ( website ) == "string" && website ) {
txt += " <a href='" + website + "'> <img src='icons/ic_info_16px.png' align='top'></a>"
}
txt += "</p><font color='#1a1a1a'>" + description + "</font><font style='font-weight: 200' color='#646464'>"
2020-03-04 16:55:40 +01:00
if ( typeof ( release_date ) == "string" && release_date )
txt += "<br>" + qsTr ( "Released: %1" ) . arg ( release_date )
if ( typeof ( url ) == "string" && url != "" && url != "internal://format" ) {
if ( typeof ( extract_sha256 ) != "undefined" && imageWriter . isCached ( url , extract_sha256 ) ) {
txt += "<br>" + qsTr ( "Cached on your computer" )
2020-06-01 17:45:41 +02:00
} else if ( url . startsWith ( "file://" ) ) {
txt += "<br>" + qsTr ( "Local file" )
2020-03-04 16:55:40 +01:00
} else {
txt += "<br>" + qsTr ( "Online - %1 GB download" ) . arg ( ( image_download_size / 1073741824 ) . toFixed ( 1 ) ) ;
}
}
txt += "</font>" ;
return txt ;
}
2021-05-07 13:10:23 +02:00
id: osText
2020-05-23 12:30:06 +02:00
2020-06-30 00:42:39 +02:00
/ *
Accessible.role: Accessible . ListItem
Accessible.name: name + ".\n" + description
Accessible.focusable: true
Accessible.focused: parent . parent . parent . ListView . view . currentIndex === index
* /
2020-05-23 12:30:06 +02:00
ToolTip {
visible: osMouseArea . containsMouse && typeof ( tooltip ) == "string" && tooltip != ""
delay: 1000
text: typeof ( tooltip ) == "string" ? tooltip : ""
clip: false
}
2020-03-04 16:55:40 +01:00
}
}
Column {
Image {
2020-06-01 17:45:41 +02:00
source: "icons/ic_chevron_right_40px.svg"
2020-03-04 16:55:40 +01:00
visible: ( typeof ( subitems ) == "object" && subitems . count ) || ( typeof ( subitems_url ) == "string" && subitems_url != "" && subitems_url != "internal://back" )
height: parent . parent . parent . height
fillMode: Image . Pad
}
}
}
MouseArea {
2020-05-23 12:30:06 +02:00
id: osMouseArea
2020-03-04 16:55:40 +01:00
anchors.fill: parent
cursorShape: Qt . PointingHandCursor
hoverEnabled: true
onEntered: {
2020-06-30 00:42:39 +02:00
bgrect . mouseOver = true
2020-03-04 16:55:40 +01:00
}
onExited: {
2020-06-30 00:42:39 +02:00
bgrect . mouseOver = false
2020-03-04 16:55:40 +01:00
}
onClicked: {
2021-05-07 13:10:23 +02:00
if ( osText . hoveredLink ) {
Qt . openUrlExternally ( osText . hoveredLink )
} else {
selectOSitem ( model )
}
2020-03-04 16:55:40 +01:00
}
}
}
}
/ *
2021-02-26 14:51:09 +01:00
Popup for storage device selection
2020-03-04 16:55:40 +01:00
* /
Popup {
id: dstpopup
x: 50
y: 25
width: parent . width - 100
height: parent . height - 50
padding: 0
closePolicy: Popup . CloseOnEscape | Popup . CloseOnPressOutside
2020-07-02 23:31:20 +02:00
onClosed: imageWriter . stopDriveListPolling ( )
2020-03-04 16:55:40 +01:00
// background of title
Rectangle {
color: "#f5f5f5"
anchors.right: parent . right
anchors.top: parent . top
height: 35
width: parent . width
}
// line under title
Rectangle {
color: "#afafaf"
width: parent . width
y: 35
implicitHeight: 1
}
Text {
text: "X"
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: {
dstpopup . close ( )
}
}
}
ColumnLayout {
spacing: 10
Text {
2021-02-26 14:51:09 +01:00
text: qsTr ( "Storage" )
2020-03-04 16:55:40 +01:00
horizontalAlignment: Text . AlignHCenter
verticalAlignment: Text . AlignVCenter
Layout.fillWidth: true
Layout.topMargin: 10
font.family: roboto . name
font.bold: true
}
Item {
clip: true
2021-03-26 15:55:54 +01:00
Layout.preferredWidth: dstlist . width
Layout.preferredHeight: dstlist . height
2020-03-04 16:55:40 +01:00
ListView {
id: dstlist
model: driveListModel
delegate: dstdelegate
width: window . width - 100
2020-05-23 12:30:06 +02:00
height: window . height - 100
2020-03-04 16:55:40 +01:00
boundsBehavior: Flickable . StopAtBounds
2020-06-30 00:42:39 +02:00
highlight: Rectangle { color: "lightsteelblue" ; radius: 5 }
2020-03-04 16:55:40 +01:00
ScrollBar.vertical: ScrollBar {
width: 10
2020-05-23 13:02:38 +02:00
policy: dstlist . contentHeight > dstlist . height ? ScrollBar.AlwaysOn : ScrollBar . AsNeeded
2020-03-04 16:55:40 +01:00
}
2020-06-30 00:42:39 +02:00
Keys.onSpacePressed: {
if ( currentIndex == - 1 )
return
2020-07-03 21:16:49 +02:00
selectDstItem ( currentItem )
2020-06-30 00:42:39 +02:00
}
Accessible.onPressAction: {
if ( currentIndex == - 1 )
return
2020-07-03 21:16:49 +02:00
selectDstItem ( currentItem )
2020-06-30 00:42:39 +02:00
}
2020-03-04 16:55:40 +01:00
}
}
}
}
Component {
id: dstdelegate
Item {
width: window . width - 100
height: 60
2020-06-30 00:42:39 +02:00
Accessible.name: {
var txt = description + " - " + ( size / 1000000000 ) . toFixed ( 1 ) + " 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
2020-03-04 16:55:40 +01:00
Rectangle {
id: dstbgrect
anchors.fill: parent
color: "#f5f5f5"
2020-06-30 00:42:39 +02:00
visible: mouseOver && parent . ListView . view . currentIndex !== index
property bool mouseOver: false
2020-03-04 16:55:40 +01:00
}
Rectangle {
id: dstborderrect
implicitHeight: 1
implicitWidth: parent . width
color: "#dcdcdc"
y: parent . height
}
Row {
leftPadding: 25
Column {
width: 64
Image {
2020-06-01 17:45:41 +02:00
source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg"
2020-03-04 16:55:40 +01:00
verticalAlignment: Image . AlignVCenter
height: parent . parent . parent . height
fillMode: Image . Pad
}
}
Column {
width: parent . parent . width - 64
Text {
textFormat: Text . StyledText
height: parent . parent . parent . height
verticalAlignment: Text . AlignVCenter
font.family: roboto . name
text: {
2020-07-03 21:16:49 +02:00
var sizeStr = ( size / 1000000000 ) . toFixed ( 1 ) + " GB" ;
var txt ;
if ( isReadOnly ) {
txt = "<p><font size='4' color='grey'>" + description + " - " + sizeStr + "</font></p>"
txt += "<font color='grey'>"
if ( mountpoints . length > 0 ) {
txt += qsTr ( "Mounted as %1" ) . arg ( mountpoints . join ( ", " ) ) + " "
}
txt += qsTr ( "[WRITE PROTECTED]" ) + "</font>"
} else {
txt = "<p><font size='4'>" + description + " - " + sizeStr + "</font></p>"
if ( mountpoints . length > 0 ) {
txt += "<font color='grey'>" + qsTr ( "Mounted as %1" ) . arg ( mountpoints . join ( ", " ) ) + "</font>"
}
2020-03-04 16:55:40 +01:00
}
return txt ;
}
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt . PointingHandCursor
hoverEnabled: true
onEntered: {
2020-06-30 00:42:39 +02:00
dstbgrect . mouseOver = true
2020-03-04 16:55:40 +01:00
}
onExited: {
2020-06-30 00:42:39 +02:00
dstbgrect . mouseOver = false
2020-03-04 16:55:40 +01:00
}
onClicked: {
2020-07-03 21:16:49 +02:00
selectDstItem ( model )
2020-03-04 16:55:40 +01:00
}
}
}
}
2020-06-30 00:42:39 +02:00
MsgPopup {
2020-03-04 16:55:40 +01:00
id: msgpopup
2020-06-30 00:42:39 +02:00
}
2020-03-04 16:55:40 +01:00
2020-06-30 00:42:39 +02:00
MsgPopup {
id: quitpopup
continueButton: false
yesButton: true
noButton: true
title: qsTr ( "Are you sure you want to quit?" )
text: qsTr ( "Raspberry Pi Imager is still busy.<br>Are you sure you want to quit?" )
onYes: {
Qt . quit ( )
2020-03-04 16:55:40 +01:00
}
2020-06-30 00:42:39 +02:00
}
2020-03-04 16:55:40 +01:00
2020-06-30 00:42:39 +02:00
MsgPopup {
id: confirmwritepopup
continueButton: false
yesButton: true
noButton: true
title: qsTr ( "Warning" )
onYes: {
2021-11-22 00:21:30 +01:00
langbar . visible = false
2020-06-30 00:42:39 +02:00
writebutton . enabled = false
2021-11-18 20:48:24 +01:00
customizebutton . visible = false
2020-06-30 00:42:39 +02:00
cancelwritebutton . enabled = true
cancelwritebutton . visible = true
cancelverifybutton . enabled = true
2020-11-19 18:10:05 +01:00
progressText . text = qsTr ( "Preparing to write..." ) ;
2020-06-30 00:42:39 +02:00
progressText . visible = true
progressBar . visible = true
progressBar . indeterminate = true
progressBar . Material . accent = "#ffffff"
osbutton . enabled = false
dstbutton . enabled = false
imageWriter . setVerifyEnabled ( true )
imageWriter . startWrite ( )
2020-03-04 16:55:40 +01:00
}
2020-06-30 00:42:39 +02:00
function askForConfirmation ( )
{
text = qsTr ( "All existing data on '%1' will be erased.<br>Are you sure you want to continue?" ) . arg ( dstbutton . text )
openPopup ( )
2020-03-04 16:55:40 +01:00
}
}
2020-11-23 19:23:20 +01:00
MsgPopup {
id: updatepopup
continueButton: false
yesButton: true
noButton: true
property url url
title: qsTr ( "Update available" )
text: qsTr ( "There is a newer version of Imager available.<br>Would you like to visit the website to download it?" )
onYes: {
Qt . openUrlExternally ( url )
}
}
2021-01-17 17:43:17 +01:00
OptionsPopup {
id: optionspopup
}
2021-01-20 13:04:18 +01:00
UseSavedSettingsPopup {
id: usesavedsettingspopup
onYes: {
optionspopup . initialize ( )
optionspopup . applySettings ( )
confirmwritepopup . askForConfirmation ( )
}
onNo: {
imageWriter . clearSavedCustomizationSettings ( )
confirmwritepopup . askForConfirmation ( )
}
onEditSettings: {
optionspopup . openPopup ( )
}
}
2020-03-04 16:55:40 +01:00
/* Utility functions */
function httpRequest ( url , callback ) {
var xhr = new XMLHttpRequest ( ) ;
xhr . timeout = 5000
xhr . onreadystatechange = ( function ( x ) {
return function ( ) {
if ( x . readyState === x . DONE )
{
if ( x . status === 200 )
{
callback ( x )
}
else
{
onError ( qsTr ( "Error downloading OS list from Internet" ) )
}
}
}
} ) ( xhr )
xhr . open ( "GET" , url )
xhr . send ( )
}
/* Slots for signals imagewrite emits */
function onDownloadProgress ( now , total ) {
var newPos
if ( total ) {
newPos = now / ( total + 1 )
} else {
newPos = 0
}
2020-05-23 12:30:06 +02:00
if ( progressBar . value !== newPos ) {
if ( progressText . text === qsTr ( "Cancelling..." ) )
2020-03-04 16:55:40 +01:00
return
progressText . text = qsTr ( "Writing... %1%" ) . arg ( Math . floor ( newPos * 100 ) )
progressBar . indeterminate = false
progressBar . value = newPos
}
}
function onVerifyProgress ( now , total ) {
var newPos
if ( total ) {
newPos = now / total
} else {
newPos = 0
}
2020-05-23 12:30:06 +02:00
if ( progressBar . value !== newPos ) {
2020-03-04 16:55:40 +01:00
if ( cancelwritebutton . visible ) {
cancelwritebutton . visible = false
cancelverifybutton . visible = true
}
2020-05-23 12:30:06 +02:00
if ( progressText . text === qsTr ( "Finalizing..." ) )
2020-03-04 16:55:40 +01:00
return
progressText . text = qsTr ( "Verifying... %1%" ) . arg ( Math . floor ( newPos * 100 ) )
progressBar . Material . accent = "#6cc04a"
progressBar . value = newPos
}
}
2020-11-19 18:10:05 +01:00
function onPreparationStatusUpdate ( msg ) {
progressText . text = qsTr ( "Preparing to write... (%1)" ) . arg ( msg )
}
2020-03-04 16:55:40 +01:00
function resetWriteButton ( ) {
progressText . visible = false
progressBar . visible = false
2021-11-18 20:48:24 +01:00
customizebutton . visible = imageWriter . imageSupportsCustomization ( )
2020-03-04 16:55:40 +01:00
osbutton . enabled = true
dstbutton . enabled = true
writebutton . visible = true
writebutton . enabled = imageWriter . readyToWrite ( )
cancelwritebutton . visible = false
cancelverifybutton . visible = false
}
function onError ( msg ) {
2020-06-30 00:42:39 +02:00
msgpopup . title = qsTr ( "Error" )
msgpopup . text = msg
msgpopup . openPopup ( )
2020-03-04 16:55:40 +01:00
resetWriteButton ( )
}
function onSuccess ( ) {
2020-06-30 00:42:39 +02:00
msgpopup . title = qsTr ( "Write Successful" )
2020-05-23 14:12:23 +02:00
if ( osbutton . text === qsTr ( "Erase" ) )
2020-06-30 00:42:39 +02:00
msgpopup . text = qsTr ( "<b>%1</b> has been erased<br><br>You can now remove the SD card from the reader" ) . arg ( dstbutton . text )
2020-05-23 14:12:23 +02:00
else
2020-06-30 00:42:39 +02:00
msgpopup . text = qsTr ( "<b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader" ) . arg ( osbutton . text ) . arg ( dstbutton . text )
2021-03-06 11:40:46 +01:00
if ( imageWriter . isEmbeddedMode ( ) ) {
msgpopup . continueButton = false
msgpopup . quitButton = true
}
2020-06-30 00:42:39 +02:00
msgpopup . openPopup ( )
2020-03-04 16:55:40 +01:00
imageWriter . setDst ( "" )
2021-02-26 14:51:09 +01:00
dstbutton . text = qsTr ( "CHOOSE STORAGE" )
2020-03-04 16:55:40 +01:00
resetWriteButton ( )
}
function onFileSelected ( file ) {
imageWriter . setSrc ( file )
osbutton . text = imageWriter . srcFileName ( )
ospopup . close ( )
if ( imageWriter . readyToWrite ( ) ) {
writebutton . enabled = true
}
2021-11-18 20:48:24 +01:00
customizebutton . visible = imageWriter . imageSupportsCustomization ( )
2020-03-04 16:55:40 +01:00
}
function onCancelled ( ) {
resetWriteButton ( )
}
function onFinalizing ( ) {
progressText . text = qsTr ( "Finalizing..." )
}
2020-05-25 00:36:16 +02:00
2020-12-01 15:52:33 +01:00
function oslistFromJson ( o ) {
var lang_country = Qt . locale ( ) . name
if ( "os_list_" + lang_country in o ) {
return o [ "os_list_" + lang_country ]
}
if ( lang_country . includes ( "_" ) ) {
var lang = lang_country . substr ( 0 , lang_country . indexOf ( "_" ) )
if ( "os_list_" + lang in o ) {
return o [ "os_list_" + lang ]
}
}
if ( ! "os_list" in o ) {
onError ( qsTr ( "Error parsing os_list.json" ) )
return false
}
return o [ "os_list" ]
}
2021-11-23 02:53:50 +01:00
function selectNamedOS ( name , collection )
{
for ( var i = 0 ; i < collection . count ; i ++ ) {
var os = collection . get ( i )
if ( typeof ( os . subitems ) !== "undefined" ) {
selectNamedOS ( name , os . subitems )
}
else if ( typeof ( os . url ) !== "undefined" && name === os . name ) {
selectOSitem ( os , false )
break
}
}
}
2020-05-25 00:36:16 +02:00
function fetchOSlist ( ) {
httpRequest ( imageWriter . constantOsListUrl ( ) , function ( x ) {
var o = JSON . parse ( x . responseText )
2020-12-01 15:52:33 +01:00
var oslist = oslistFromJson ( o )
if ( oslist === false )
return
2020-05-25 00:36:16 +02:00
for ( var i in oslist ) {
osmodel . insert ( osmodel . count - 2 , oslist [ i ] )
}
2020-11-23 19:23:20 +01:00
if ( "imager" in o ) {
var imager = o [ "imager" ]
if ( "latest_version" in imager && "url" in imager ) {
2021-03-06 11:40:46 +01:00
if ( ! imageWriter . isEmbeddedMode ( ) && imageWriter . isVersionNewer ( imager [ "latest_version" ] ) ) {
2020-11-23 19:23:20 +01:00
updatepopup . url = imager [ "url" ]
updatepopup . openPopup ( )
}
}
2021-11-23 02:53:50 +01:00
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 ( )
}
}
2020-11-23 19:23:20 +01:00
}
2020-05-25 00:36:16 +02:00
} )
}
2020-06-30 00:42:39 +02:00
2021-11-23 02:53:50 +01:00
Timer {
/* Verify if default drive is in our list after 100 ms */
id: setDefaultDest
property string drive : ""
interval: 100
onTriggered: {
for ( var i = 0 ; i < driveListModel . rowCount ( ) ; i ++ )
{
/ * F I X M E : t h e r e s h o u l d b e a b e t t e r w a y t o i t e r a t e d r i v e l i s t t h a n
fetch data by numeric role number * /
if ( driveListModel . data ( 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 ) ,
readonly: false
} )
break
}
}
}
}
2020-12-01 15:52:33 +01:00
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
}
2020-06-30 00:42:39 +02:00
function selectOSitem ( d , selectFirstSubitem )
{
if ( typeof ( d . subitems ) == "object" && d . subitems . count ) {
2020-12-01 15:52:33 +01:00
var m = newSublist ( )
2020-06-30 00:42:39 +02:00
for ( var i = 0 ; i < d . subitems . count ; i ++ )
{
2020-12-01 15:52:33 +01:00
m . append ( d . subitems . get ( i ) )
2020-06-30 00:42:39 +02:00
}
2020-12-01 15:52:33 +01:00
osswipeview . itemAt ( osswipeview . currentIndex + 1 ) . currentIndex = ( selectFirstSubitem === true ) ? 0 : - 1
osswipeview . incrementCurrentIndex ( )
2020-11-26 22:26:15 +01:00
ospopup . categorySelected = d . name
2020-06-30 00:42:39 +02:00
} else if ( typeof ( d . subitems_url ) == "string" && d . subitems_url !== "" ) {
if ( d . subitems_url === "internal://back" )
{
2020-12-01 15:52:33 +01:00
osswipeview . decrementCurrentIndex ( )
2020-11-26 22:26:15 +01:00
ospopup . categorySelected = ""
2020-06-30 00:42:39 +02:00
}
else
{
2020-11-26 22:26:15 +01:00
ospopup . categorySelected = d . name
2020-11-23 17:59:18 +01:00
var suburl = d . subitems_url
2020-12-01 15:52:33 +01:00
var m = newSublist ( )
2020-06-30 00:42:39 +02:00
2020-11-23 17:59:18 +01:00
httpRequest ( suburl , function ( x ) {
2020-06-30 00:42:39 +02:00
var o = JSON . parse ( x . responseText )
2020-12-01 15:52:33 +01:00
var oslist = oslistFromJson ( o )
if ( oslist === false )
return
2020-06-30 00:42:39 +02:00
for ( var i in oslist ) {
2020-12-01 15:52:33 +01:00
m . append ( oslist [ i ] )
2020-06-30 00:42:39 +02:00
}
} )
2020-12-01 15:52:33 +01:00
osswipeview . itemAt ( osswipeview . currentIndex + 1 ) . currentIndex = ( selectFirstSubitem === true ) ? 0 : - 1
osswipeview . incrementCurrentIndex ( )
2020-06-30 00:42:39 +02:00
}
} else if ( d . url === "" ) {
if ( ! imageWriter . isEmbeddedMode ( ) ) {
imageWriter . openFileDialog ( )
}
else {
if ( imageWriter . mountUsbSourceMedia ( ) ) {
2020-12-01 15:52:33 +01:00
var m = newSublist ( )
2020-06-30 00:42:39 +02:00
var oslist = JSON . parse ( imageWriter . getUsbSourceOSlist ( ) )
for ( var i in oslist ) {
2020-12-01 15:52:33 +01:00
m . append ( oslist [ i ] )
2020-06-30 00:42:39 +02:00
}
2020-12-01 15:52:33 +01:00
osswipeview . itemAt ( osswipeview . currentIndex + 1 ) . currentIndex = ( selectFirstSubitem === true ) ? 0 : - 1
osswipeview . incrementCurrentIndex ( )
2020-06-30 00:42:39 +02:00
}
else
{
onError ( qsTr ( "Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick." ) )
}
}
} else {
2021-11-18 20:48:24 +01:00
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 : "" )
2020-06-30 00:42:39 +02:00
osbutton . text = d . name
ospopup . close ( )
if ( imageWriter . readyToWrite ( ) ) {
writebutton . enabled = true
}
2021-11-18 20:48:24 +01:00
customizebutton . visible = imageWriter . imageSupportsCustomization ( )
2020-06-30 00:42:39 +02:00
}
}
2020-07-03 21:16:49 +02:00
function selectDstItem ( d ) {
if ( d . isReadOnly ) {
onError ( qsTr ( "SD card is write protected.<br>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
}
}
2020-03-04 16:55:40 +01:00
}