diff --git a/.idea/pikman-update-manager.iml b/.idea/bin-apt.iml
similarity index 82%
rename from .idea/pikman-update-manager.iml
rename to .idea/bin-apt.iml
index cf84ae4..af5ed37 100644
--- a/.idea/pikman-update-manager.iml
+++ b/.idea/bin-apt.iml
@@ -3,6 +3,7 @@
+
diff --git a/.idea/bin-gui.iml b/.idea/bin-gui.iml
new file mode 100644
index 0000000..6272d62
--- /dev/null
+++ b/.idea/bin-gui.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/lib.iml b/.idea/lib.iml
new file mode 100644
index 0000000..b91f2c4
--- /dev/null
+++ b/.idea/lib.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index e2a5217..1ee0cb2 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,9 @@
-
+
+
+
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index e6f330c..f204661 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -8,17 +8,17 @@
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
+
@@ -49,28 +49,35 @@
- {
- "keyToString": {
- "ASKED_ADD_EXTERNAL_FILES": "true",
- "Cargo.Build all.executor": "Run",
- "Cargo.Run apt_update.executor": "Run",
- "Cargo.Run pikman-update-manager.executor": "Run",
- "Cargo.Test pikman-update-manager.executor": "Run",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.rust.reset.selective.auto.import": "true",
- "git-widget-placeholder": "main",
- "last_opened_file_path": "/home/ward/RustroverProjects/pkg-pikman-update-manager",
- "node.js.detected.package.eslint": "true",
- "node.js.selected.package.eslint": "(autodetect)",
- "nodejs_package_manager_path": "npm",
- "org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
- "org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "",
- "org.rust.disableDetachedFileInspection/home/ward/RustroverProjects/pkg-pikman-update-manager/src/apt_update_progress_socket/lib.rs": "true",
- "org.rust.first.attach.projects": "true"
+
+}]]>
+
@@ -82,7 +89,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -117,6 +144,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -172,6 +233,10 @@
+
+
+
+
@@ -181,4 +246,15 @@
+
+
+
+
+ file://$PROJECT_DIR$/src/bin/gui/flatpak_update_page/mod.rs
+ 492
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 9c5fc76..304188d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -160,7 +160,7 @@ checksum = "797fd5a634dcb0ad0d7d583df794deb0a236d88e759cd34b7da20198c6c9d145"
dependencies = [
"bitflags 2.6.0",
"cairo-sys-rs",
- "glib",
+ "glib 0.20.0",
"libc",
"thiserror",
]
@@ -171,9 +171,9 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f"
dependencies = [
- "glib-sys",
+ "glib-sys 0.20.0",
"libc",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -503,8 +503,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28bb53ecb56857c683c9ec859908e076dd3969c7d67598bd8b1ce095d211304a"
dependencies = [
"gdk-pixbuf-sys",
- "gio",
- "glib",
+ "gio 0.20.0",
+ "glib 0.20.0",
"libc",
]
@@ -514,11 +514,11 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f6681a0c1330d1d3968bec1529f7172d62819ef0bdbb0d18022320654158b03"
dependencies = [
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.0",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"libc",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -530,8 +530,8 @@ dependencies = [
"cairo-rs",
"gdk-pixbuf",
"gdk4-sys",
- "gio",
- "glib",
+ "gio 0.20.0",
+ "glib 0.20.0",
"libc",
"pango",
]
@@ -544,13 +544,13 @@ checksum = "a67576c8ec012156d7f680e201a807b4432a77babb3157e0555e990ab6bcd878"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.0",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"libc",
"pango-sys",
"pkg-config",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -568,6 +568,24 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+[[package]]
+name = "gio"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys 0.19.8",
+ "glib 0.19.9",
+ "libc",
+ "pin-project-lite",
+ "smallvec 1.13.2",
+ "thiserror",
+]
+
[[package]]
name = "gio"
version = "0.20.0"
@@ -578,27 +596,62 @@ dependencies = [
"futures-core",
"futures-io",
"futures-util",
- "gio-sys",
- "glib",
+ "gio-sys 0.20.0",
+ "glib 0.20.0",
"libc",
"pin-project-lite",
"smallvec 1.13.2",
"thiserror",
]
+[[package]]
+name = "gio-sys"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef"
+dependencies = [
+ "glib-sys 0.19.8",
+ "gobject-sys 0.19.8",
+ "libc",
+ "system-deps 6.2.2",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "gio-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4feb96b31c32730ea3e1e89aecd2e4e37ecb1c473ad8f685e3430a159419f63"
dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"libc",
- "system-deps",
+ "system-deps 7.0.1",
"windows-sys 0.52.0",
]
+[[package]]
+name = "glib"
+version = "0.19.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44"
+dependencies = [
+ "bitflags 2.6.0",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys 0.19.8",
+ "glib-macros 0.19.9",
+ "glib-sys 0.19.8",
+ "gobject-sys 0.19.8",
+ "libc",
+ "memchr",
+ "smallvec 1.13.2",
+ "thiserror",
+]
+
[[package]]
name = "glib"
version = "0.20.0"
@@ -611,16 +664,29 @@ dependencies = [
"futures-executor",
"futures-task",
"futures-util",
- "gio-sys",
- "glib-macros",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.0",
+ "glib-macros 0.20.0",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"libc",
"memchr",
"smallvec 1.13.2",
"thiserror",
]
+[[package]]
+name = "glib-macros"
+version = "0.19.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7"
+dependencies = [
+ "heck",
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "glib-macros"
version = "0.20.0"
@@ -634,6 +700,16 @@ dependencies = [
"syn",
]
+[[package]]
+name = "glib-sys"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5"
+dependencies = [
+ "libc",
+ "system-deps 6.2.2",
+]
+
[[package]]
name = "glib-sys"
version = "0.20.0"
@@ -641,7 +717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4958c26e5a01c9af00dea669a97369eccbec29a8e6d125c24ea2d85ee7467b60"
dependencies = [
"libc",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -674,15 +750,26 @@ dependencies = [
"walkdir",
]
+[[package]]
+name = "gobject-sys"
+version = "0.19.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e"
+dependencies = [
+ "glib-sys 0.19.8",
+ "libc",
+ "system-deps 6.2.2",
+]
+
[[package]]
name = "gobject-sys"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6908864f5ffff15b56df7e90346863904f49b949337ed0456b9287af61903b8"
dependencies = [
- "glib-sys",
+ "glib-sys 0.20.0",
"libc",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -691,7 +778,7 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630e940ad5824f90221d6579043a9cd1f8bec86b4a17faaf7827d58eb16e8c1f"
dependencies = [
- "glib",
+ "glib 0.20.0",
"graphene-sys",
"libc",
]
@@ -702,10 +789,10 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8fade7b754982f47ebbed241fd2680816fdd4598321784da10b9e1168836a"
dependencies = [
- "glib-sys",
+ "glib-sys 0.20.0",
"libc",
"pkg-config",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -716,7 +803,7 @@ checksum = "1f3cf2091e1af185b347b3450817d93dea6fe435df7abd4c2cd7fb5bcb4cfda8"
dependencies = [
"cairo-rs",
"gdk4",
- "glib",
+ "glib 0.20.0",
"graphene-rs",
"gsk4-sys",
"libc",
@@ -731,12 +818,12 @@ checksum = "6aa69614a26d8760c186c3690f1b0fbb917572ca23ef83137445770ceddf8cde"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"graphene-sys",
"libc",
"pango-sys",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -750,8 +837,8 @@ dependencies = [
"futures-channel",
"gdk-pixbuf",
"gdk4",
- "gio",
- "glib",
+ "gio 0.20.0",
+ "glib 0.20.0",
"graphene-rs",
"gsk4",
"gtk4-macros",
@@ -781,14 +868,14 @@ dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gdk4-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.0",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"graphene-sys",
"gsk4-sys",
"libc",
"pango-sys",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -930,8 +1017,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878"
dependencies = [
"gdk4",
- "gio",
- "glib",
+ "gio 0.20.0",
+ "glib 0.20.0",
"gtk4",
"libadwaita-sys",
"libc",
@@ -945,13 +1032,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6"
dependencies = [
"gdk4-sys",
- "gio-sys",
- "glib-sys",
- "gobject-sys",
+ "gio-sys 0.20.0",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"gtk4-sys",
"libc",
"pango-sys",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -960,6 +1047,32 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+[[package]]
+name = "libflatpak"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a0466e5bc49c8a9d03c1daf924833474640bf3b334b01efde4234e77efe9780"
+dependencies = [
+ "gio 0.19.8",
+ "glib 0.19.9",
+ "libc",
+ "libflatpak-sys",
+]
+
+[[package]]
+name = "libflatpak-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0022c1b20fa3959a52299cd531c6afc9433182f612b1210f18776a7857a58f2b"
+dependencies = [
+ "gio-sys 0.19.8",
+ "glib-sys 0.19.8",
+ "gobject-sys 0.19.8",
+ "libc",
+ "pkg-config",
+ "system-deps 6.2.2",
+]
+
[[package]]
name = "link-cplusplus"
version = "1.0.9"
@@ -1149,8 +1262,8 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54768854025df6903061d0084fd9702a253ddfd60db7d9b751d43b76689a7f0a"
dependencies = [
- "gio",
- "glib",
+ "gio 0.20.0",
+ "glib 0.20.0",
"libc",
"pango-sys",
]
@@ -1161,10 +1274,10 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07cc57d10cee4ec661f718a6902cee18c2f4cfae08e87e5a390525946913390"
dependencies = [
- "glib-sys",
- "gobject-sys",
+ "glib-sys 0.20.0",
+ "gobject-sys 0.20.0",
"libc",
- "system-deps",
+ "system-deps 7.0.1",
]
[[package]]
@@ -1239,6 +1352,7 @@ dependencies = [
"futures 0.3.30",
"gtk4",
"libadwaita",
+ "libflatpak",
"lock_api 0.4.12",
"pretty-bytes",
"rust-apt",
@@ -1607,6 +1721,19 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml 0.8.14",
+ "version-compare",
+]
+
[[package]]
name = "system-deps"
version = "7.0.1"
diff --git a/Cargo.toml b/Cargo.toml
index 81384f0..8ef248e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,4 +36,5 @@ pretty-bytes = "0.2.2"
crossbeam-utils = "0.8.20"
chrono = "0.4.38"
lock_api = "0.4.2"
+libflatpak = { version = "0.5.0", package = "libflatpak", features = ["v1_11_1"] }
diff --git a/locales/en_US.json b/locales/en_US.json
index fa909a6..efa03b2 100644
--- a/locales/en_US.json
+++ b/locales/en_US.json
@@ -53,5 +53,14 @@
"banner_text_no_internet": "Warning: No Internet Connection!",
"refresh_button_tooltip_text": "Refresh Opened Page",
"apt_update_page_title": "Native Updates (APT)",
- "packages_no_viewport_page_title": "All Native APT Packages are Up to date!"
+ "apt_packages_no_viewport_page_title": "All Native APT Packages are Up to date!",
+ "summary_button_label": "Summary",
+ "flatpak_extra_info_ref_name": "Ref Codename",
+ "flatpak_extra_info_download_size": "Download Size",
+ "flatpak_extra_info_installed_size": "Size on Disk",
+ "remote_label_label": "Remote",
+ "flatpak_update_dialog_heading": "Flatpak Appstream Sync",
+ "flatpak_update_dialog_retry_label": "Retry",
+ "flatpak_update_dialog_status_failed": "Flatpak Appstream Sync: Failed",
+ "flatpak_packages_no_viewport_page_title": "All Flatpak Packages are Up to date!"
}
\ No newline at end of file
diff --git a/src/bin/gui/apt_package_row/imp.rs b/src/bin/gui/apt_package_row/imp.rs
index eace26f..330341d 100644
--- a/src/bin/gui/apt_package_row/imp.rs
+++ b/src/bin/gui/apt_package_row/imp.rs
@@ -468,8 +468,8 @@ fn description_stack_page(package_description: &str) -> gtk::Box {
.buffer(&description_text_buffer)
.hexpand(true)
.vexpand(true)
- .margin_top(15)
- .margin_bottom(15)
+ .margin_top(0)
+ .margin_bottom(10)
.margin_start(15)
.margin_end(15)
.editable(false)
diff --git a/src/bin/gui/apt_update_page/mod.rs b/src/bin/gui/apt_update_page/mod.rs
index 89280ae..bbfc940 100644
--- a/src/bin/gui/apt_update_page/mod.rs
+++ b/src/bin/gui/apt_update_page/mod.rs
@@ -116,7 +116,7 @@ pub fn apt_update_page(
let packages_no_viewport_page = adw::StatusPage::builder()
.icon_name("emblem-default-symbolic")
- .title(t!("packages_no_viewport_page_title"))
+ .title(t!("apt_packages_no_viewport_page_title"))
.hexpand(true)
.vexpand(true)
.build();
diff --git a/src/bin/gui/build_ui/mod.rs b/src/bin/gui/build_ui/mod.rs
index 13b14fa..d6b4a75 100644
--- a/src/bin/gui/build_ui/mod.rs
+++ b/src/bin/gui/build_ui/mod.rs
@@ -1,5 +1,6 @@
use crate::apt_update_page;
use crate::config::{APP_GITHUB, APP_ICON, APP_ID, VERSION};
+use crate::flatpak_update_page;
use adw::prelude::*;
use adw::*;
use gtk::glib::{clone, MainContext};
@@ -130,8 +131,30 @@ pub fn build_ui(app: &Application) {
// Apt Update Page
let apt_retry_signal_action = gio::SimpleAction::new("retry", None);
+ //let apt_update_view_stack_bin = Bin::builder()
+ // .child(&apt_update_page::apt_update_page(
+ // window.clone(),
+ // &apt_retry_signal_action,
+ // ))
+ // .build();
+
+ // apt_retry_signal_action.connect_activate(clone!(
+ // #[weak]
+ // window,
+ // #[strong]
+ // apt_retry_signal_action,
+ // #[strong]
+ // apt_update_view_stack_bin,
+ // move |_, _| {
+ // apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page(
+ // window,
+ // &apt_retry_signal_action,
+ // )));
+ // }
+ // ));
+
let apt_update_view_stack_bin = Bin::builder()
- .child(&apt_update_page::apt_update_page(
+ .child(&flatpak_update_page::flatpak_update_page(
window.clone(),
&apt_retry_signal_action,
))
@@ -145,7 +168,7 @@ pub fn build_ui(app: &Application) {
#[strong]
apt_update_view_stack_bin,
move |_, _| {
- apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page(
+ apt_update_view_stack_bin.set_child(Some(&flatpak_update_page::flatpak_update_page(
window,
&apt_retry_signal_action,
)));
diff --git a/src/bin/gui/flatpak_ref_row/imp.rs b/src/bin/gui/flatpak_ref_row/imp.rs
new file mode 100644
index 0000000..3f11b0d
--- /dev/null
+++ b/src/bin/gui/flatpak_ref_row/imp.rs
@@ -0,0 +1,486 @@
+use std::{cell::RefCell, sync::OnceLock};
+
+use adw::*;
+use adw::{prelude::*, subclass::prelude::*};
+use glib::{clone, subclass::Signal, Properties};
+use gtk::*;
+use pretty_bytes::converter::convert;
+use std::env;
+
+// ANCHOR: custom_button
+// Object holding the state
+#[derive(Properties, Default)]
+#[properties(wrapper_type = super::FlatpakRefRow)]
+pub struct FlatpakRefRow {
+ #[property(get, set)]
+ flatref_name: RefCell,
+ #[property(get, set)]
+ flatref_arch: RefCell,
+ #[property(get, set)]
+ flatref_ref_name: RefCell,
+ #[property(get, set)]
+ flatref_summary: RefCell,
+ #[property(get, set)]
+ flatref_remote_name: RefCell,
+ #[property(get, set)]
+ flatref_installed_size_installed: RefCell,
+ #[property(get, set)]
+ flatref_installed_size_remote: RefCell,
+ #[property(get, set)]
+ flatref_download_size: RefCell,
+ #[property(get, set)]
+ flatref_ref_format: RefCell,
+ #[property(get, set)]
+ flatref_is_system: RefCell,
+ #[property(get, set)]
+ flatref_marked: RefCell,
+}
+// ANCHOR_END: custom_button
+
+// The central trait for subclassing a GObject
+#[glib::object_subclass]
+impl ObjectSubclass for FlatpakRefRow {
+ const NAME: &'static str = "FlatpakRefRow";
+ type Type = super::FlatpakRefRow;
+ type ParentType = ExpanderRow;
+}
+
+// ANCHOR: object_impl
+// Trait shared by all GObjects
+#[glib::derived_properties]
+impl ObjectImpl for FlatpakRefRow {
+ fn signals() -> &'static [Signal] {
+ static SIGNALS: OnceLock> = OnceLock::new();
+ SIGNALS.get_or_init(|| {
+ vec![
+ Signal::builder("checkbutton-toggled").build(),
+ Signal::builder("checkbutton-untoggled").build(),
+ ]
+ })
+ }
+ fn constructed(&self) {
+ let current_locale = match env::var_os("LANG") {
+ Some(v) => v
+ .into_string()
+ .unwrap()
+ .chars()
+ .take_while(|&ch| ch != '.')
+ .collect::(),
+ None => panic!("$LANG is not set"),
+ };
+ rust_i18n::set_locale(¤t_locale);
+
+ self.parent_constructed();
+
+ // Bind label to number
+ // `SYNC_CREATE` ensures that the label will be immediately set
+ let obj = self.obj();
+
+ let prefix_box = Box::new(Orientation::Vertical, 0);
+
+ let expandable_box = Box::new(Orientation::Vertical, 0);
+
+ obj.connect_flatref_name_notify(clone!(
+ #[weak]
+ prefix_box,
+ #[weak]
+ expandable_box,
+ #[strong]
+ obj,
+ move |_| {
+ remove_all_children_from_box(&prefix_box);
+ remove_all_children_from_box(&expandable_box);
+ //
+ let flatref_name = obj.flatref_name();
+ let flatref_arch = obj.flatref_arch();
+ let flatref_ref_name = obj.flatref_ref_name();
+ let flatref_summary = obj.flatref_summary();
+ let flatref_remote_name = obj.flatref_remote_name();
+ let flatref_installed_size_installed = obj.flatref_installed_size_installed();
+ let flatref_installed_size_remote = obj.flatref_installed_size_remote();
+ let flatref_download_size = obj.flatref_download_size();
+ let flatref_ref_format = obj.flatref_download_size();
+ let flatref_is_system = obj.flatref_is_system();
+ let flatref_marked = obj.flatref_marked();
+ //
+ create_prefix_content(
+ &prefix_box,
+ &flatref_name,
+ &flatref_arch,
+ flatref_is_system,
+ &flatref_remote_name,
+ );
+ //
+ create_expandable_content(
+ &obj,
+ &expandable_box,
+ flatref_ref_name,
+ flatref_summary,
+ flatref_download_size,
+ flatref_installed_size_remote,
+ );
+ }
+ ));
+
+ obj.add_prefix(&prefix_box);
+ obj.add_row(&expandable_box);
+
+ let suffix_toggle = CheckButton::builder()
+ .tooltip_text(t!("mark_for_update"))
+ .halign(Align::Center)
+ .valign(Align::Center)
+ .hexpand(false)
+ .vexpand(false)
+ .build();
+
+ suffix_toggle.connect_toggled(clone!(
+ #[weak]
+ obj,
+ #[weak]
+ suffix_toggle,
+ move |_| {
+ if suffix_toggle.is_active() {
+ obj.emit_by_name::<()>("checkbutton-toggled", &[]);
+ } else {
+ obj.emit_by_name::<()>("checkbutton-untoggled", &[]);
+ }
+ }
+ ));
+
+ obj.add_suffix(&suffix_toggle);
+
+ let obj = self.obj();
+ obj.bind_property("flatref_marked", &suffix_toggle, "active")
+ .sync_create()
+ .bidirectional()
+ .build();
+
+ // turn on by default
+ obj.set_property("flatref_marked", true)
+ }
+}
+// Trait shared by all widgets
+impl WidgetImpl for FlatpakRefRow {}
+
+// Trait shared by all buttons
+// Trait shared by all buttons
+
+impl ListBoxRowImpl for FlatpakRefRow {}
+impl PreferencesRowImpl for FlatpakRefRow {}
+impl ExpanderRowImpl for FlatpakRefRow {}
+
+fn create_remote_badge(remote_name: &str) -> ListBox {
+ let remote_label = Label::builder()
+ .halign(Align::Start)
+ .hexpand(false)
+ .label(format!("{}: {}", t!("remote_label_label"), remote_name))
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(5)
+ .margin_top(5)
+ .build();
+
+ let boxedlist = ListBox::builder()
+ .selection_mode(SelectionMode::None)
+ .halign(Align::Start)
+ .valign(Align::End)
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(10)
+ .build();
+
+ boxedlist.add_css_class("boxed-list");
+ boxedlist.append(&remote_label);
+ boxedlist
+}
+fn create_arch_badge(arch: &str) -> ListBox {
+ let arch_label = Label::builder()
+ .halign(Align::Start)
+ .hexpand(false)
+ .label(format!("{}: {}", t!("arch_label_label"), arch))
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(5)
+ .margin_top(5)
+ .build();
+
+ let boxedlist = ListBox::builder()
+ .selection_mode(SelectionMode::None)
+ .halign(Align::Start)
+ .valign(Align::End)
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(10)
+ .build();
+
+ boxedlist.add_css_class("boxed-list");
+ boxedlist.append(&arch_label);
+ boxedlist
+}
+
+fn create_system_badge(is_system: bool) -> ListBox {
+ let system_label = Label::builder()
+ .halign(Align::Start)
+ .hexpand(false)
+ .label(match is_system {
+ true => "System",
+ false => "User",
+ })
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(5)
+ .margin_top(5)
+ .build();
+
+ let boxedlist = ListBox::builder()
+ .selection_mode(SelectionMode::None)
+ .halign(Align::Start)
+ .valign(Align::End)
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(10)
+ .build();
+
+ boxedlist.add_css_class("boxed-list");
+ boxedlist.append(&system_label);
+ boxedlist
+}
+
+fn remove_all_children_from_box(parent: >k::Box) {
+ while let Some(child) = parent.last_child() {
+ parent.remove(&child);
+ }
+}
+
+fn create_prefix_content(
+ prefix_box: >k::Box,
+ flatref_name: &str,
+ flatref_arch: &str,
+ flatref_is_system: bool,
+ flatref_remote_name: &str,
+) {
+ let package_label = Label::builder()
+ .halign(Align::Start)
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(5)
+ .margin_top(5)
+ .label(flatref_name)
+ .build();
+ package_label.add_css_class("size-20-bold-text");
+ let prefix_badge_box = Box::new(Orientation::Horizontal, 0);
+ prefix_badge_box.append(&create_remote_badge(flatref_remote_name));
+ prefix_badge_box.append(&create_arch_badge(flatref_arch));
+ prefix_badge_box.append(&create_system_badge(flatref_is_system));
+ prefix_box.append(&package_label);
+ prefix_box.append(&prefix_badge_box);
+}
+
+fn create_expandable_content(
+ flatpak_package_row: &impl IsA,
+ expandable_box: >k::Box,
+ flatref_ref_name: String,
+ flatref_summary: String,
+ flatref_download_size: u64,
+ flatref_installed_size_remote: u64,
+) {
+ let expandable_page_selection_box = Box::builder()
+ .orientation(Orientation::Horizontal)
+ .hexpand(false)
+ .vexpand(false)
+ .halign(Align::Start)
+ .valign(Align::Start)
+ .margin_bottom(10)
+ .margin_top(10)
+ .margin_start(10)
+ .margin_end(10)
+ .build();
+ expandable_page_selection_box.add_css_class("linked");
+ //
+ let summary_page_button = ToggleButton::builder()
+ .label(t!("summary_button_label"))
+ .active(true)
+ .build();
+ let extra_info_page_button = ToggleButton::builder()
+ .label(t!("extra_info_page_button_label"))
+ .group(&summary_page_button)
+ .build();
+ expandable_page_selection_box.append(&summary_page_button);
+ expandable_page_selection_box.append(&extra_info_page_button);
+ //
+ expandable_box.append(&expandable_page_selection_box);
+ //
+ let expandable_bin = Bin::builder().hexpand(true).vexpand(true).build();
+ //
+ summary_page_button.connect_clicked(clone!(
+ #[strong]
+ expandable_bin,
+ #[strong]
+ summary_page_button,
+ move |_| {
+ if summary_page_button.is_active() {
+ expandable_bin.set_child(Some(&summary_stack_page(&flatref_summary)));
+ }
+ }
+ ));
+
+ extra_info_page_button.connect_clicked(clone!(
+ #[strong]
+ expandable_bin,
+ #[strong]
+ extra_info_page_button,
+ move |_| {
+ if extra_info_page_button.is_active() {
+ expandable_bin.set_child(Some(&extra_info_stack_page(
+ &flatref_ref_name,
+ flatref_download_size,
+ flatref_installed_size_remote,
+ )));
+ }
+ }
+ ));
+
+ flatpak_package_row.connect_expanded_notify(clone!(
+ #[strong]
+ expandable_bin,
+ #[strong]
+ expandable_box,
+ #[strong]
+ flatpak_package_row,
+ #[strong]
+ summary_page_button,
+ move |_| {
+ if flatpak_package_row.property("expanded") {
+ summary_page_button.set_active(true);
+ summary_page_button.emit_by_name::<()>("clicked", &[]);
+ expandable_box.append(&expandable_bin)
+ } else {
+ expandable_box.remove(&expandable_bin)
+ }
+ }
+ ));
+}
+
+fn summary_stack_page(flatref_summary: &str) -> gtk::Box {
+ let summary_content_box = Box::builder()
+ .hexpand(true)
+ .vexpand(true)
+ .orientation(Orientation::Vertical)
+ .build();
+ let summary_text_buffer = TextBuffer::builder()
+ .text(flatref_summary.to_owned() + "\n")
+ .build();
+ let summary_text_view = TextView::builder()
+ .buffer(&summary_text_buffer)
+ .hexpand(true)
+ .vexpand(true)
+ .margin_top(0)
+ .margin_bottom(10)
+ .margin_start(15)
+ .margin_end(15)
+ .editable(false)
+ .build();
+ summary_content_box.append(&summary_text_view);
+ summary_content_box
+}
+
+fn extra_info_stack_page(
+ flatref_ref_name: &str,
+ flatref_download_size: u64,
+ flatref_installed_size_remote: u64,
+) -> gtk::Box {
+ let extra_info_badges_content_box = Box::builder()
+ .hexpand(true)
+ .vexpand(true)
+ .orientation(Orientation::Vertical)
+ .build();
+ let extra_info_badges_size_group = SizeGroup::new(SizeGroupMode::Both);
+ let extra_info_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both);
+ let extra_info_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both);
+ let package_size = flatref_download_size as f64;
+ let package_installed_size = flatref_installed_size_remote as f64;
+ extra_info_badges_content_box.append(&create_color_badge(
+ &t!("flatpak_extra_info_ref_name").to_string(),
+ flatref_ref_name,
+ "background-accent-bg",
+ &extra_info_badges_size_group,
+ &extra_info_badges_size_group0,
+ &extra_info_badges_size_group1,
+ ));
+ extra_info_badges_content_box.append(&create_color_badge(
+ &t!("flatpak_extra_info_download_size").to_string(),
+ &convert(package_size),
+ "background-accent-bg",
+ &extra_info_badges_size_group,
+ &extra_info_badges_size_group0,
+ &extra_info_badges_size_group1,
+ ));
+ extra_info_badges_content_box.append(&create_color_badge(
+ &t!("flatpak_extra_info_installed_size").to_string(),
+ &convert(package_installed_size),
+ "background-accent-bg",
+ &extra_info_badges_size_group,
+ &extra_info_badges_size_group0,
+ &extra_info_badges_size_group1,
+ ));
+ extra_info_badges_content_box
+}
+fn create_color_badge(
+ label0_text: &str,
+ label1_text: &str,
+ css_style: &str,
+ group_size: &SizeGroup,
+ group_size0: &SizeGroup,
+ group_size1: &SizeGroup,
+) -> ListBox {
+ let badge_box = Box::builder().build();
+
+ let label0 = Label::builder()
+ .label(label0_text)
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(1)
+ .margin_top(1)
+ .valign(Align::Center)
+ .halign(Align::Center)
+ .hexpand(true)
+ .vexpand(true)
+ .build();
+ group_size0.add_widget(&label0);
+
+ let label_separator = Separator::builder().build();
+
+ let label1 = Label::builder()
+ .label(label1_text)
+ .margin_start(3)
+ .margin_end(0)
+ .margin_bottom(1)
+ .margin_top(1)
+ .valign(Align::Center)
+ .halign(Align::Center)
+ .hexpand(true)
+ .vexpand(true)
+ .build();
+ group_size1.add_widget(&label1);
+
+ label1.add_css_class(css_style);
+
+ badge_box.append(&label0);
+ badge_box.append(&label_separator);
+ badge_box.append(&label1);
+
+ let boxedlist = ListBox::builder()
+ .selection_mode(SelectionMode::None)
+ .halign(Align::Start)
+ .valign(Align::Start)
+ .margin_start(10)
+ .margin_end(10)
+ .margin_bottom(10)
+ .margin_top(10)
+ .build();
+
+ boxedlist.add_css_class("boxed-list");
+ boxedlist.append(&badge_box);
+ group_size.add_widget(&boxedlist);
+ boxedlist
+}
diff --git a/src/bin/gui/flatpak_ref_row/mod.rs b/src/bin/gui/flatpak_ref_row/mod.rs
new file mode 100644
index 0000000..7f03258
--- /dev/null
+++ b/src/bin/gui/flatpak_ref_row/mod.rs
@@ -0,0 +1,54 @@
+mod imp;
+
+use crate::flatpak_update_page::FlatpakRefStruct;
+use glib::Object;
+use gtk::glib;
+
+glib::wrapper! {
+ pub struct FlatpakRefRow(ObjectSubclass)
+ @extends adw::ExpanderRow, gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow,
+ @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
+}
+
+impl FlatpakRefRow {
+ pub fn new(flatref: &FlatpakRefStruct) -> Self {
+ let flatref = flatref.clone();
+ Object::builder()
+ .property("flatref-name", flatref.name)
+ .property("flatref-arch", flatref.arch)
+ .property("flatref-ref-name", flatref.ref_name)
+ .property("flatref-summary", flatref.summary)
+ .property("flatref-remote-name", flatref.remote_name)
+ .property(
+ "flatref-installed-size-installed",
+ flatref.installed_size_installed,
+ )
+ .property(
+ "flatref-installed-size-remote",
+ flatref.installed_size_remote,
+ )
+ .property("flatref-download-size", flatref.download_size)
+ .property("flatref-ref-format", flatref.ref_format)
+ .property("flatref-is-system", flatref.is_system)
+ .build()
+ }
+}
+// ANCHOR_END: mod
+
+impl Default for FlatpakRefRow {
+ fn default() -> Self {
+ Self::new(&FlatpakRefStruct {
+ ref_name: "??".to_owned(),
+ name: "??".to_owned(),
+ arch: "??".to_owned(),
+ summary: "??".to_owned(),
+ remote_name: "??".to_owned(),
+ installed_size_installed: 0,
+ installed_size_remote: 0,
+ download_size: 0,
+ ref_format: "??".to_owned(),
+ is_system: false,
+ is_last: false,
+ })
+ }
+}
diff --git a/src/bin/gui/flatpak_update_page/mod.rs b/src/bin/gui/flatpak_update_page/mod.rs
new file mode 100644
index 0000000..dd4e2b3
--- /dev/null
+++ b/src/bin/gui/flatpak_update_page/mod.rs
@@ -0,0 +1,743 @@
+mod process;
+
+use crate::apt_package_row::AptPackageRow;
+use crate::flatpak_ref_row::FlatpakRefRow;
+use adw::gio::SimpleAction;
+use adw::prelude::*;
+use gtk::glib::*;
+use gtk::*;
+use libflatpak::prelude::*;
+use libflatpak::InstalledRef;
+use std::cell::RefCell;
+use std::process::Command;
+use std::rc::Rc;
+use std::thread;
+
+#[derive(Clone)]
+pub struct FlatpakRefStruct {
+ pub ref_name: String,
+ pub name: String,
+ pub arch: String,
+ pub summary: String,
+ pub remote_name: String,
+ pub installed_size_installed: u64,
+ pub installed_size_remote: u64,
+ pub download_size: u64,
+ pub ref_format: String,
+ pub is_system: bool,
+ pub is_last: bool,
+}
+pub fn flatpak_update_page(
+ window: adw::ApplicationWindow,
+ retry_signal_action: &SimpleAction,
+) -> gtk::Box {
+ let (appstream_sync_percent_sender, appstream_sync_percent_receiver) =
+ async_channel::unbounded::();
+ let appstream_sync_percent_sender = appstream_sync_percent_sender.clone();
+ let (appstream_sync_status_sender, appstream_sync_status_receiver) =
+ async_channel::unbounded::();
+ let appstream_sync_status_sender = appstream_sync_status_sender.clone();
+ let appstream_sync_status_sender_clone0 = appstream_sync_status_sender.clone();
+
+ let system_refs_for_upgrade_vec: Rc>> = Rc::new(RefCell::new(Vec::new()));
+
+ let user_refs_for_upgrade_vec: Rc>> = Rc::new(RefCell::new(Vec::new()));
+
+ let cancellable_no = libflatpak::gio::Cancellable::NONE;
+
+ thread::spawn(move || {
+ let cancellable_no = libflatpak::gio::Cancellable::NONE;
+ let flatpak_system_installation =
+ libflatpak::Installation::new_user(cancellable_no).unwrap();
+ if let Ok(remotes) =
+ libflatpak::Installation::list_remotes(&flatpak_system_installation, cancellable_no)
+ {
+ for remote in remotes {
+ if remote.is_disabled() {
+ continue;
+ };
+ let mut remote_clousre = |status: &str, progress: u32, _: bool| {
+ appstream_sync_percent_sender
+ .send_blocking(progress)
+ .expect("appstream_sync_percent_receiver closed");
+ appstream_sync_status_sender
+ .send_blocking(format!(
+ "System - {}: {}",
+ remote.name().unwrap_or("Unknown remote".into()),
+ status
+ ))
+ .expect("appstream_sync_status_receiver closed");
+ };
+ match libflatpak::Installation::update_appstream_full_sync(
+ &flatpak_system_installation,
+ &remote.name().unwrap(),
+ None,
+ Some(&mut remote_clousre),
+ cancellable_no,
+ ) {
+ Ok(_) => {}
+ Err(e) => {
+ appstream_sync_status_sender
+ .send_blocking(e.to_string())
+ .expect("appstream_sync_status_receiver closed");
+ appstream_sync_status_sender
+ .send_blocking("FN_OVERRIDE_FAILED".to_owned())
+ .expect("appstream_sync_status_receiver closed");
+ break;
+ }
+ }
+ }
+ }
+ let flatpak_user_installation = libflatpak::Installation::new_user(cancellable_no).unwrap();
+ if let Ok(remotes) =
+ libflatpak::Installation::list_remotes(&flatpak_user_installation, cancellable_no)
+ {
+ for remote in remotes {
+ if remote.is_disabled() {
+ continue;
+ };
+ let mut remote_clousre = |status: &str, progress: u32, _: bool| {
+ appstream_sync_percent_sender
+ .send_blocking(progress)
+ .expect("appstream_sync_percent_receiver closed");
+ appstream_sync_status_sender
+ .send_blocking(format!(
+ "User - {}: {}",
+ remote.name().unwrap_or("Unknown remote".into()),
+ status
+ ))
+ .expect("appstream_sync_status_receiver closed");
+ };
+ match libflatpak::Installation::update_appstream_full_sync(
+ &flatpak_user_installation,
+ &remote.name().unwrap(),
+ None,
+ Some(&mut remote_clousre),
+ cancellable_no,
+ ) {
+ Ok(_) => {
+ appstream_sync_status_sender
+ .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned())
+ .expect("appstream_sync_status_receiver closed");
+ }
+ Err(e) => {
+ appstream_sync_status_sender
+ .send_blocking(e.to_string())
+ .expect("appstream_sync_status_receiver closed");
+ appstream_sync_status_sender
+ .send_blocking("FN_OVERRIDE_FAILED".to_owned())
+ .expect("appstream_sync_status_receiver closed");
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ let main_box = Box::builder()
+ .hexpand(true)
+ .vexpand(true)
+ .orientation(Orientation::Vertical)
+ .build();
+
+ let searchbar = SearchEntry::builder()
+ .search_delay(500)
+ .margin_top(15)
+ .margin_bottom(15)
+ .margin_end(30)
+ .margin_start(30)
+ .build();
+ searchbar.add_css_class("rounded-all-25");
+
+ let packages_boxedlist = ListBox::builder()
+ .selection_mode(SelectionMode::None)
+ .margin_end(15)
+ .margin_start(15)
+ .sensitive(false)
+ .build();
+ packages_boxedlist.add_css_class("boxed-list");
+
+ let packages_viewport = ScrolledWindow::builder()
+ .vexpand(true)
+ .hexpand(true)
+ .margin_bottom(15)
+ .margin_top(15)
+ .margin_end(15)
+ .margin_start(15)
+ .height_request(390)
+ .child(&packages_boxedlist)
+ .build();
+
+ let packages_no_viewport_page = adw::StatusPage::builder()
+ .icon_name("emblem-default-symbolic")
+ .title(t!("flatpak_packages_no_viewport_page_title"))
+ .hexpand(true)
+ .vexpand(true)
+ .build();
+
+ let viewport_bin = adw::Bin::builder()
+ .child(&packages_no_viewport_page)
+ .build();
+
+ let flatpak_update_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
+
+ let flatpak_update_dialog_progress_bar =
+ ProgressBar::builder().show_text(true).hexpand(true).build();
+
+ let flatpak_update_dialog_spinner = Spinner::builder()
+ .hexpand(true)
+ .valign(Align::Start)
+ .halign(Align::Center)
+ .spinning(true)
+ .height_request(128)
+ .width_request(128)
+ .build();
+
+ flatpak_update_dialog_child_box.append(&flatpak_update_dialog_spinner);
+ flatpak_update_dialog_child_box.append(&flatpak_update_dialog_progress_bar);
+
+ let flatpak_update_dialog = adw::MessageDialog::builder()
+ .transient_for(&window)
+ .extra_child(&flatpak_update_dialog_child_box)
+ .heading(t!("flatpak_update_dialog_heading"))
+ .width_request(500)
+ .build();
+
+ flatpak_update_dialog.add_response(
+ "flatpak_update_dialog_retry",
+ &t!("flatpak_update_dialog_retry_label").to_string(),
+ );
+
+ flatpak_update_dialog.set_response_appearance(
+ "flatpak_update_dialog_retry",
+ adw::ResponseAppearance::Suggested,
+ );
+
+ flatpak_update_dialog.set_response_enabled("flatpak_update_dialog_retry", false);
+
+ let retry_signal_action0 = retry_signal_action.clone();
+
+ flatpak_update_dialog
+ .clone()
+ .choose(None::<&gio::Cancellable>, move |choice| {
+ if choice == "flatpak_update_dialog_retry" {
+ retry_signal_action0.activate(None);
+ }
+ });
+
+ let bottom_bar = Box::builder().valign(Align::End).build();
+
+ let select_button = Button::builder()
+ .halign(Align::End)
+ .valign(Align::Center)
+ .hexpand(true)
+ .margin_start(10)
+ .margin_end(10)
+ .margin_bottom(15)
+ .label(t!("select_button_deselect_all"))
+ .build();
+
+ select_button.connect_clicked(clone!(
+ #[weak]
+ select_button,
+ #[weak]
+ packages_boxedlist,
+ move |_| {
+ let select_button_label = select_button.label().unwrap();
+ let value_to_mark = if select_button_label == t!("select_button_select_all").to_string()
+ {
+ true
+ } else if select_button_label == t!("select_button_deselect_all").to_string() {
+ false
+ } else {
+ panic!("Unexpected label on selection button")
+ };
+ set_all_flatpak_row_marks_to(&packages_boxedlist, value_to_mark)
+ }
+ ));
+
+ let update_button = Button::builder()
+ .halign(Align::End)
+ .valign(Align::Center)
+ .hexpand(false)
+ .margin_start(10)
+ .margin_end(30)
+ .margin_bottom(15)
+ .label(t!("update_button_label"))
+ .build();
+ update_button.add_css_class("destructive-action");
+
+ update_button.connect_clicked(clone!(
+ #[weak]
+ window,
+ #[weak]
+ retry_signal_action,
+ #[strong]
+ system_refs_for_upgrade_vec,
+ #[strong]
+ user_refs_for_upgrade_vec,
+ move |_| {
+ dbg!(&system_refs_for_upgrade_vec);
+ dbg!(&user_refs_for_upgrade_vec);
+ }
+ ));
+
+ bottom_bar.append(&select_button);
+ bottom_bar.append(&update_button);
+
+ let appstream_sync_percent_server_context = MainContext::default();
+ // The main loop executes the asynchronous block
+ appstream_sync_percent_server_context.spawn_local(clone!(
+ #[weak]
+ flatpak_update_dialog_progress_bar,
+ async move {
+ while let Ok(state) = appstream_sync_percent_receiver.recv().await {
+ flatpak_update_dialog_progress_bar.set_fraction(state as f64 / 100.0)
+ }
+ }
+ ));
+
+ let appstream_sync_status_server_context = MainContext::default();
+ // The main loop executes the asynchronous block
+ appstream_sync_status_server_context.spawn_local(clone!(
+ #[weak]
+ flatpak_update_dialog,
+ #[weak]
+ flatpak_update_dialog_child_box,
+ #[strong]
+ packages_boxedlist,
+ #[strong]
+ viewport_bin,
+ #[strong]
+ packages_viewport,
+ async move {
+ while let Ok(state) = appstream_sync_status_receiver.recv().await {
+ match state.as_ref() {
+ "FN_OVERRIDE_SUCCESSFUL" => {
+ let flatpak_system_installation =
+ libflatpak::Installation::new_system(cancellable_no).unwrap();
+ let flatpak_system_updates = flatpak_system_installation
+ .list_installed_refs_for_update(cancellable_no)
+ .unwrap();
+ let flatpak_system_transaction = libflatpak::Transaction::for_installation(
+ &flatpak_system_installation,
+ cancellable_no,
+ )
+ .unwrap();
+ //
+ let flatpak_user_installation =
+ libflatpak::Installation::new_user(cancellable_no).unwrap();
+ let flatpak_user_updates = flatpak_user_installation
+ .list_installed_refs_for_update(cancellable_no)
+ .unwrap();
+ let flatpak_user_transaction = libflatpak::Transaction::for_installation(
+ &flatpak_user_installation,
+ cancellable_no,
+ )
+ .unwrap();
+ //
+ let mut system_last_triggered = false;
+ let mut user_last_triggered = false;
+ //
+ if !flatpak_system_updates.is_empty() || !flatpak_user_updates.is_empty() {
+ viewport_bin.set_child(Some(&packages_viewport));
+ //
+ let mut flatpak_system_updates_iter =
+ &mut flatpak_system_updates.iter().peekable();
+ //
+ while let Some(flatpak_ref) = flatpak_system_updates_iter.next() {
+ let mut remote_flatpak_ref: Option = None;
+ while let Ok(remotes) = libflatpak::Installation::list_remotes(
+ &flatpak_system_installation,
+ cancellable_no,
+ ) {
+ for remote in remotes {
+ if remote.is_disabled() {
+ continue;
+ };
+ match libflatpak::Installation::fetch_remote_ref_sync(
+ &flatpak_system_installation,
+ &match remote.name() {
+ Some(t) => t,
+ None => continue,
+ },
+ flatpak_ref.kind(),
+ &match flatpak_ref.name() {
+ Some(t) => t,
+ None => continue,
+ },
+ flatpak_ref.arch().as_deref(),
+ flatpak_ref.branch().as_deref(),
+ cancellable_no,
+ ) {
+ Ok(t) => {
+ remote_flatpak_ref = Some(t);
+ break;
+ }
+ Err(_) => continue,
+ }
+ }
+ if remote_flatpak_ref.is_some() {
+ break;
+ }
+ }
+ let flatref_struct = FlatpakRefStruct {
+ ref_name: flatpak_ref
+ .name()
+ .unwrap_or("Unknown".into())
+ .to_string(),
+ name: flatpak_ref
+ .appdata_name()
+ .unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into()))
+ .to_string(),
+ arch: flatpak_ref
+ .arch()
+ .unwrap_or("Unknown Arch".into())
+ .to_string(),
+ summary: flatpak_ref
+ .appdata_summary()
+ .unwrap_or("No Summary".into())
+ .to_string(),
+ remote_name: match remote_flatpak_ref {
+ Some(ref t) => {
+ t.remote_name().unwrap_or("Unknown".into()).to_string()
+ }
+ None => "Unknown".into(),
+ },
+ installed_size_installed: flatpak_ref.installed_size(),
+ installed_size_remote: match remote_flatpak_ref {
+ Some(ref t) => t.installed_size(),
+ None => 0,
+ },
+ download_size: match remote_flatpak_ref {
+ Some(t) => t.download_size(),
+ None => 0,
+ },
+ ref_format: flatpak_ref.format_ref().unwrap().into(),
+ is_system: true,
+ is_last: flatpak_system_updates_iter.peek().is_none(),
+ };
+
+ system_refs_for_upgrade_vec
+ .borrow_mut()
+ .push(flatpak_ref.format_ref().unwrap().into());
+
+ let flatpak_row = FlatpakRefRow::new(&flatref_struct);
+
+ flatpak_row.connect_closure(
+ "checkbutton-toggled",
+ false,
+ closure_local!(
+ #[strong]
+ select_button,
+ #[strong]
+ update_button,
+ #[strong]
+ packages_boxedlist,
+ #[strong]
+ system_refs_for_upgrade_vec,
+ move |flatpak_row: FlatpakRefRow| {
+ if is_widget_select_all_ready(&packages_boxedlist) {
+ select_button.set_label(
+ &t!("select_button_select_all").to_string(),
+ );
+ } else {
+ select_button.set_label(
+ &t!("select_button_deselect_all").to_string(),
+ );
+ }
+ update_button.set_sensitive(!is_all_children_unmarked(
+ &packages_boxedlist,
+ ));
+ system_refs_for_upgrade_vec
+ .borrow_mut()
+ .push(flatpak_row.flatref_ref_format());
+ }
+ ),
+ );
+ flatpak_row.connect_closure(
+ "checkbutton-untoggled",
+ false,
+ closure_local!(
+ #[strong]
+ select_button,
+ #[strong]
+ update_button,
+ #[strong]
+ packages_boxedlist,
+ #[strong]
+ system_refs_for_upgrade_vec,
+ move |flatpak_row: FlatpakRefRow| {
+ select_button.set_label(
+ &t!("select_button_select_all").to_string(),
+ );
+ update_button.set_sensitive(!is_all_children_unmarked(
+ &packages_boxedlist,
+ ));
+ system_refs_for_upgrade_vec
+ .borrow_mut()
+ .retain(|x| x != &flatpak_row.flatref_ref_format());
+ }
+ ),
+ );
+
+ packages_boxedlist.append(&flatpak_row);
+ if flatpak_system_updates.is_empty()
+ || flatref_struct.is_system && flatref_struct.is_last
+ {
+ system_last_triggered = true
+ }
+ }
+ //
+ let mut flatpak_user_updates_iter =
+ &mut flatpak_user_updates.iter().peekable();
+ //
+ while let Some(flatpak_ref) = flatpak_user_updates_iter.next() {
+ let mut remote_flatpak_ref: Option = None;
+ while let Ok(remotes) = libflatpak::Installation::list_remotes(
+ &flatpak_system_installation,
+ cancellable_no,
+ ) {
+ for remote in remotes {
+ if remote.is_disabled() {
+ continue;
+ };
+ match libflatpak::Installation::fetch_remote_ref_sync(
+ &flatpak_system_installation,
+ &match remote.name() {
+ Some(t) => t,
+ None => continue,
+ },
+ flatpak_ref.kind(),
+ &match flatpak_ref.name() {
+ Some(t) => t,
+ None => continue,
+ },
+ flatpak_ref.arch().as_deref(),
+ flatpak_ref.branch().as_deref(),
+ cancellable_no,
+ ) {
+ Ok(t) => {
+ remote_flatpak_ref = Some(t);
+ break;
+ }
+ Err(_) => continue,
+ }
+ }
+ if remote_flatpak_ref.is_some() {
+ break;
+ }
+ }
+ let flatref_struct = FlatpakRefStruct {
+ ref_name: flatpak_ref
+ .name()
+ .unwrap_or("Unknown".into())
+ .to_string(),
+ name: flatpak_ref
+ .appdata_name()
+ .unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into()))
+ .to_string(),
+ arch: flatpak_ref
+ .arch()
+ .unwrap_or("Unknown Arch".into())
+ .to_string(),
+ summary: flatpak_ref
+ .appdata_summary()
+ .unwrap_or("No Summary".into())
+ .to_string(),
+ remote_name: match remote_flatpak_ref {
+ Some(ref t) => {
+ t.remote_name().unwrap_or("Unknown".into()).to_string()
+ }
+ None => "Unknown".into(),
+ },
+ installed_size_installed: flatpak_ref.installed_size(),
+ installed_size_remote: match remote_flatpak_ref {
+ Some(ref t) => t.installed_size(),
+ None => 0,
+ },
+ download_size: match remote_flatpak_ref {
+ Some(t) => t.download_size(),
+ None => 0,
+ },
+ ref_format: flatpak_ref.format_ref().unwrap().into(),
+ is_system: false,
+ is_last: flatpak_user_updates_iter.peek().is_none(),
+ };
+
+ user_refs_for_upgrade_vec
+ .borrow_mut()
+ .push(flatpak_ref.format_ref().unwrap().into());
+
+ let flatpak_row = FlatpakRefRow::new(&flatref_struct);
+
+ flatpak_row.connect_closure(
+ "checkbutton-toggled",
+ false,
+ closure_local!(
+ #[strong]
+ select_button,
+ #[strong]
+ update_button,
+ #[strong]
+ packages_boxedlist,
+ #[strong]
+ user_refs_for_upgrade_vec,
+ move |flatpak_row: FlatpakRefRow| {
+ if is_widget_select_all_ready(&packages_boxedlist) {
+ select_button.set_label(
+ &t!("select_button_select_all").to_string(),
+ );
+ } else {
+ select_button.set_label(
+ &t!("select_button_deselect_all").to_string(),
+ );
+ }
+ update_button.set_sensitive(!is_all_children_unmarked(
+ &packages_boxedlist,
+ ));
+ user_refs_for_upgrade_vec
+ .borrow_mut()
+ .push(flatpak_row.flatref_ref_format());
+ }
+ ),
+ );
+ flatpak_row.connect_closure(
+ "checkbutton-untoggled",
+ false,
+ closure_local!(
+ #[strong]
+ select_button,
+ #[strong]
+ update_button,
+ #[strong]
+ packages_boxedlist,
+ #[strong]
+ user_refs_for_upgrade_vec,
+ move |flatpak_row: FlatpakRefRow| {
+ select_button.set_label(
+ &t!("select_button_select_all").to_string(),
+ );
+ update_button.set_sensitive(!is_all_children_unmarked(
+ &packages_boxedlist,
+ ));
+ user_refs_for_upgrade_vec
+ .borrow_mut()
+ .retain(|x| x != &flatpak_row.flatref_ref_format());
+ }
+ ),
+ );
+ packages_boxedlist.append(&flatpak_row);
+ if flatpak_user_updates.is_empty()
+ || !flatref_struct.is_system && flatref_struct.is_last
+ {
+ user_last_triggered = true
+ }
+ }
+ if user_last_triggered && system_last_triggered {
+ packages_boxedlist.set_sensitive(true);
+ }
+ }
+ flatpak_update_dialog.close();
+ }
+ "FN_OVERRIDE_FAILED" => {
+ flatpak_update_dialog_child_box.set_visible(false);
+ flatpak_update_dialog.set_extra_child(Some(
+ &Image::builder()
+ .pixel_size(128)
+ .icon_name("dialog-error-symbolic")
+ .halign(Align::Center)
+ .build(),
+ ));
+ flatpak_update_dialog.set_title(Some(
+ &t!("flatpak_update_dialog_status_failed").to_string(),
+ ));
+ flatpak_update_dialog
+ .set_response_enabled("flatpak_update_dialog_retry", true);
+ }
+ _ => flatpak_update_dialog.set_body(&state),
+ }
+ }
+ }
+ ));
+
+ searchbar.connect_search_changed(clone!(
+ #[weak]
+ searchbar,
+ #[weak]
+ packages_boxedlist,
+ move |_| {
+ let mut counter = packages_boxedlist.first_child();
+ while let Some(row) = counter {
+ if row.widget_name() == "FlatpakRefRow" {
+ if !searchbar.text().is_empty() {
+ if row
+ .property::("flatref-name")
+ .to_lowercase()
+ .contains(&searchbar.text().to_string().to_lowercase())
+ || row
+ .property::("flatref-ref-name")
+ .to_lowercase()
+ .contains(&searchbar.text().to_string().to_lowercase())
+ {
+ row.set_property("visible", true);
+ searchbar.grab_focus();
+ } else {
+ row.set_property("visible", false);
+ }
+ } else {
+ row.set_property("visible", true);
+ }
+ }
+ counter = row.next_sibling();
+ }
+ }
+ ));
+
+ main_box.append(&searchbar);
+ main_box.append(&viewport_bin);
+ main_box.append(&bottom_bar);
+
+ flatpak_update_dialog.present();
+ main_box
+}
+
+fn is_widget_select_all_ready(parent_listbox: &impl adw::prelude::IsA) -> bool {
+ let mut is_ready = false;
+ let mut child_counter = parent_listbox.borrow().first_child();
+ while let Some(child) = child_counter {
+ let next_child = child.next_sibling();
+ let downcast = child.downcast::().unwrap();
+ if !downcast.flatref_marked() {
+ is_ready = true;
+ break;
+ }
+ child_counter = next_child
+ }
+ is_ready
+}
+
+fn is_all_children_unmarked(parent_listbox: &impl adw::prelude::IsA) -> bool {
+ let mut is_all_unmarked = true;
+ let mut child_counter = parent_listbox.borrow().first_child();
+ while let Some(child) = child_counter {
+ let next_child = child.next_sibling();
+ let downcast = child.downcast::().unwrap();
+ if downcast.flatref_marked() {
+ is_all_unmarked = false;
+ break;
+ }
+ child_counter = next_child
+ }
+ is_all_unmarked
+}
+
+fn set_all_flatpak_row_marks_to(parent_listbox: &impl adw::prelude::IsA, value: bool) {
+ let mut child_counter = parent_listbox.borrow().first_child();
+ while let Some(child) = child_counter {
+ let next_child = child.next_sibling();
+ let downcast = child.downcast::().unwrap();
+ downcast.set_flatref_marked(value);
+ child_counter = next_child
+ }
+}
diff --git a/src/bin/gui/flatpak_update_page/process.rs b/src/bin/gui/flatpak_update_page/process.rs
new file mode 100644
index 0000000..5ed3cb3
--- /dev/null
+++ b/src/bin/gui/flatpak_update_page/process.rs
@@ -0,0 +1,617 @@
+use adw::gio::SimpleAction;
+use adw::prelude::*;
+use gtk::glib::*;
+use gtk::*;
+use pika_unixsocket_tools::pika_unixsocket_tools::{
+ start_socket_server, start_socket_server_no_log,
+};
+use pretty_bytes::converter::convert;
+use rust_apt::cache::Upgrade;
+use rust_apt::new_cache;
+use serde::Serialize;
+use serde_json::Value;
+use std::cell::RefCell;
+use std::path::Path;
+use std::process::Command;
+use std::rc::Rc;
+use std::thread;
+use tokio::runtime::Runtime;
+
+struct AptChangesInfo {
+ package_count_upgrade: u64,
+ package_count_install: u64,
+ package_count_downgrade: u64,
+ package_count_remove: u64,
+ total_download_size: u64,
+ total_installed_size: i64,
+}
+#[derive(Serialize)]
+struct Exclusions {
+ exclusions: Vec,
+}
+
+impl AptChangesInfo {
+ fn add_upgrade(&mut self) {
+ self.package_count_upgrade += 1;
+ }
+ fn add_install(&mut self) {
+ self.package_count_install += 1;
+ }
+ fn add_downgrade(&mut self) {
+ self.package_count_downgrade += 1;
+ }
+ fn add_remove(&mut self) {
+ self.package_count_remove += 1;
+ }
+
+ fn increase_total_download_size_by(&mut self, value: u64) {
+ self.total_download_size += value;
+ }
+
+ fn increase_total_installed_size_by(&mut self, value: u64) {
+ self.total_installed_size += value as i64;
+ }
+
+ fn decrease_total_installed_size_by(&mut self, value: u64) {
+ self.total_installed_size -= value as i64;
+ }
+}
+
+pub fn apt_process_update(
+ excluded_updates_vec: &Vec,
+ window: adw::ApplicationWindow,
+ retry_signal_action: &SimpleAction,
+) {
+ let excluded_updates_alert_dialog = adw::MessageDialog::builder()
+ .transient_for(&window)
+ .heading(t!("excluded_updates_alert_dialog_heading"))
+ .body(t!("excluded_updates_alert_dialog_body"))
+ .build();
+
+ excluded_updates_alert_dialog.add_response(
+ "excluded_updates_alert_dialog_cancel",
+ &t!("excluded_updates_alert_dialog_cancel_label").to_string(),
+ );
+
+ excluded_updates_alert_dialog.add_response(
+ "excluded_updates_alert_continue",
+ &t!("excluded_updates_alert_continue_label").to_string(),
+ );
+
+ excluded_updates_alert_dialog.set_response_appearance(
+ "excluded_updates_alert_continue",
+ adw::ResponseAppearance::Destructive,
+ );
+
+ excluded_updates_alert_dialog.set_default_response(Some("excluded_updates_alert_continue"));
+
+ let excluded_updates_alert_dialog_action =
+ SimpleAction::new("excluded_updates_alert_dialog_action", None);
+
+ excluded_updates_alert_dialog_action.connect_activate(clone!(
+ #[weak]
+ window,
+ #[weak]
+ retry_signal_action,
+ #[strong]
+ excluded_updates_vec,
+ move |_, _| { apt_confirm_window(&excluded_updates_vec, window, &retry_signal_action) }
+ ));
+
+ if excluded_updates_vec.is_empty() {
+ excluded_updates_alert_dialog_action.activate(None);
+ } else {
+ excluded_updates_alert_dialog.choose(None::<&gio::Cancellable>, move |choice| {
+ if choice == "excluded_updates_alert_continue" {
+ excluded_updates_alert_dialog_action.activate(None);
+ }
+ });
+ }
+}
+
+fn apt_confirm_window(
+ excluded_updates_vec: &Vec,
+ window: adw::ApplicationWindow,
+ retry_signal_action: &SimpleAction,
+) {
+ let to_be_removed_packages_vec: Rc>> = Rc::new(RefCell::new(Vec::new()));
+ // Emulate Apt Full Upgrade to get transaction info
+ let mut apt_changes_struct = AptChangesInfo {
+ package_count_upgrade: 0,
+ package_count_install: 0,
+ package_count_downgrade: 0,
+ package_count_remove: 0,
+ total_download_size: 0,
+ total_installed_size: 0,
+ };
+
+ let apt_cache = new_cache!().unwrap();
+ let apt_upgrade_cache = new_cache!().unwrap();
+
+ apt_cache.upgrade(Upgrade::FullUpgrade).unwrap();
+
+ for change in apt_cache.get_changes(false) {
+ if !excluded_updates_vec
+ .iter()
+ .any(|e| change.name().contains(e))
+ {
+ let pkg = apt_upgrade_cache.get(change.name()).unwrap();
+ if change.marked_upgrade() || change.marked_install() || change.marked_downgrade() {
+ pkg.mark_install(true, false);
+ } else if change.marked_delete() {
+ pkg.mark_delete(false);
+ to_be_removed_packages_vec
+ .borrow_mut()
+ .push(pkg.name().to_owned());
+ }
+ pkg.protect();
+ }
+ }
+
+ apt_upgrade_cache.resolve(true).unwrap();
+
+ println!("{}", t!("gui_changes_emu_msg_0"));
+ for change in apt_upgrade_cache.get_changes(false) {
+ if change.is_installed() {
+ apt_changes_struct
+ .decrease_total_installed_size_by(change.installed().unwrap().installed_size());
+ }
+ if change.marked_upgrade() && change.is_installed() {
+ println!("{}: {}", t!("gui_changes_emu_msg_upgrading"), change.name());
+ apt_changes_struct.add_upgrade();
+ apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size());
+ apt_changes_struct
+ .increase_total_installed_size_by(change.candidate().unwrap().installed_size());
+ } else if change.marked_install() || change.marked_upgrade() && !change.is_installed() {
+ println!(
+ "{}: {}",
+ t!("gui_changes_emu_msg_installing"),
+ change.name()
+ );
+ apt_changes_struct.add_install();
+ apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size());
+ apt_changes_struct
+ .increase_total_installed_size_by(change.candidate().unwrap().installed_size());
+ } else if change.marked_downgrade() {
+ println!(
+ "{}: {}",
+ t!("gui_changes_emu_msg_downgrading"),
+ change.name()
+ );
+ apt_changes_struct.add_downgrade();
+ apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size());
+ apt_changes_struct
+ .increase_total_installed_size_by(change.candidate().unwrap().installed_size());
+ } else if change.marked_delete() {
+ println!("{}: {}", t!("gui_changes_emu_msg_removing"), change.name());
+ apt_changes_struct.add_remove();
+ }
+ }
+
+ let apt_confirm_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
+
+ let apt_update_dialog_badges_size_group = SizeGroup::new(SizeGroupMode::Both);
+ let apt_update_dialog_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both);
+ let apt_update_dialog_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both);
+
+ apt_confirm_dialog_child_box.append(&create_color_badge(
+ &t!("package_count_upgrade_badge_label"),
+ &apt_changes_struct.package_count_upgrade.to_string(),
+ "background-accent-bg",
+ &apt_update_dialog_badges_size_group,
+ &apt_update_dialog_badges_size_group0,
+ &apt_update_dialog_badges_size_group1,
+ ));
+
+ apt_confirm_dialog_child_box.append(&create_color_badge(
+ &t!("package_count_install_badge_label"),
+ &apt_changes_struct.package_count_install.to_string(),
+ "background-accent-bg",
+ &apt_update_dialog_badges_size_group,
+ &apt_update_dialog_badges_size_group0,
+ &apt_update_dialog_badges_size_group1,
+ ));
+
+ apt_confirm_dialog_child_box.append(&create_color_badge(
+ &t!("package_count_downgrade_badge_label"),
+ &apt_changes_struct.package_count_downgrade.to_string(),
+ "background-accent-bg",
+ &apt_update_dialog_badges_size_group,
+ &apt_update_dialog_badges_size_group0,
+ &apt_update_dialog_badges_size_group1,
+ ));
+
+ apt_confirm_dialog_child_box.append(&create_color_badge(
+ &t!("package_count_remove_badge_label"),
+ &apt_changes_struct.package_count_remove.to_string(),
+ "background-accent-bg",
+ &apt_update_dialog_badges_size_group,
+ &apt_update_dialog_badges_size_group0,
+ &apt_update_dialog_badges_size_group1,
+ ));
+
+ apt_confirm_dialog_child_box.append(&create_color_badge(
+ &t!("total_download_size_badge_label"),
+ &convert(apt_changes_struct.total_download_size as f64),
+ "background-accent-bg",
+ &apt_update_dialog_badges_size_group,
+ &apt_update_dialog_badges_size_group0,
+ &apt_update_dialog_badges_size_group1,
+ ));
+
+ apt_confirm_dialog_child_box.append(&create_color_badge(
+ &t!("total_installed_size_badge_label"),
+ &convert(apt_changes_struct.total_installed_size as f64),
+ "background-accent-bg",
+ &apt_update_dialog_badges_size_group,
+ &apt_update_dialog_badges_size_group0,
+ &apt_update_dialog_badges_size_group1,
+ ));
+
+ let apt_confirm_dialog = adw::MessageDialog::builder()
+ .transient_for(&window)
+ .heading(t!("apt_confirm_dialog_heading"))
+ .body(t!("apt_confirm_dialog_body"))
+ .extra_child(&apt_confirm_dialog_child_box)
+ .build();
+
+ apt_confirm_dialog.add_response(
+ "apt_confirm_dialog_cancel",
+ &t!("apt_confirm_dialog_cancel_label").to_string(),
+ );
+
+ apt_confirm_dialog.add_response(
+ "apt_confirm_dialog_confirm",
+ &t!("apt_confirm_dialog_confirm_label").to_string(),
+ );
+
+ apt_confirm_dialog.set_response_appearance(
+ "apt_confirm_dialog_confirm",
+ adw::ResponseAppearance::Destructive,
+ );
+
+ apt_confirm_dialog.set_default_response(Some("apt_confirm_dialog_confirm"));
+ apt_confirm_dialog.set_close_response("apt_confirm_dialog_cancel");
+
+ let json_file_path = "/tmp/pika-apt-exclusions.json";
+
+ if Path::new(json_file_path).exists() {
+ std::fs::remove_file(json_file_path).expect("Failed to remove old json file");
+ }
+
+ if !excluded_updates_vec.is_empty() {
+ let exclusions_array = Exclusions {
+ exclusions: excluded_updates_vec
+ .into_iter()
+ .map(|i| serde_json::from_str(format!("{{\"package\":\"{}\"}}", i).as_str()))
+ .collect::, _>>()
+ .unwrap(),
+ };
+
+ std::fs::write(
+ json_file_path,
+ serde_json::to_string_pretty(&exclusions_array).unwrap(),
+ )
+ .expect("Failed to write to json file");
+ }
+
+ let apt_confirm_start_signal_action = SimpleAction::new("apt_confirm_start", None);
+
+ apt_confirm_start_signal_action.connect_activate(clone!(
+ #[weak]
+ window,
+ #[strong]
+ retry_signal_action,
+ #[strong]
+ apt_confirm_dialog,
+ move |_, _| {
+ let retry_signal_action0 = retry_signal_action.clone();
+ apt_confirm_dialog
+ .clone()
+ .choose(None::<&gio::Cancellable>, move |choice| {
+ if choice == "apt_confirm_dialog_confirm" {
+ apt_full_upgrade_from_socket(window, &retry_signal_action0);
+ }
+ });
+ }
+ ));
+
+ let to_be_removed_packages_borrow = to_be_removed_packages_vec.borrow();
+ if to_be_removed_packages_borrow.is_empty() {
+ apt_confirm_start_signal_action.activate(None);
+ } else {
+ let apt_remove_confirm_text_buffer = TextBuffer::builder()
+ .text(
+ to_be_removed_packages_borrow
+ .iter()
+ .map(|x| x.to_string() + "\n")
+ .collect::()
+ + "\n",
+ )
+ .build();
+
+ let apt_remove_confirm_text_view = TextView::builder()
+ .buffer(&apt_remove_confirm_text_buffer)
+ .hexpand(true)
+ .vexpand(true)
+ .margin_top(15)
+ .margin_bottom(15)
+ .margin_start(15)
+ .margin_end(15)
+ .editable(false)
+ .build();
+
+ let apt_remove_confirm_dialog = adw::MessageDialog::builder()
+ .transient_for(&window)
+ .heading(t!("apt_remove_confirm_dialog_heading"))
+ .body(t!("apt_remove_confirm_dialog_body"))
+ .extra_child(&apt_remove_confirm_text_view)
+ .build();
+
+ apt_remove_confirm_dialog.add_response(
+ "apt_remove_confirm_dialog_cancel",
+ &t!("apt_remove_confirm_dialog_cancel_label").to_string(),
+ );
+
+ apt_remove_confirm_dialog.add_response(
+ "apt_remove_confirm_dialog_confirm",
+ &t!("apt_remove_confirm_dialog_confirm_label").to_string(),
+ );
+
+ apt_remove_confirm_dialog.set_response_appearance(
+ "apt_remove_confirm_dialog_confirm",
+ adw::ResponseAppearance::Destructive,
+ );
+
+ apt_remove_confirm_dialog.set_default_response(Some("apt_remove_confirm_dialog_confirm"));
+ apt_remove_confirm_dialog.set_close_response("apt_remove_confirm_dialog_cancel");
+
+ apt_remove_confirm_dialog.choose(None::<&gio::Cancellable>, move |choice| {
+ if choice == "apt_remove_confirm_dialog_confirm" {
+ apt_confirm_start_signal_action.activate(None);
+ }
+ });
+ }
+}
+
+fn apt_full_upgrade_from_socket(
+ window: adw::ApplicationWindow,
+ retry_signal_action: &SimpleAction,
+) {
+ let (upgrade_percent_sender, upgrade_percent_receiver) = async_channel::unbounded::();
+ let upgrade_percent_sender = upgrade_percent_sender.clone();
+ let (upgrade_status_sender, upgrade_status_receiver) = async_channel::unbounded::();
+ let upgrade_status_sender = upgrade_status_sender.clone();
+ let upgrade_status_sender_clone0 = upgrade_status_sender.clone();
+
+ let log_file_path = format!(
+ "/tmp/pika-apt-upgrade_{}.log",
+ chrono::offset::Local::now().format("%Y-%m-%d_%H:%M")
+ );
+ let log_file_path_clone0 = log_file_path.clone();
+
+ thread::spawn(move || {
+ Runtime::new().unwrap().block_on(start_socket_server_no_log(
+ upgrade_percent_sender,
+ "/tmp/pika_apt_upgrade_percent.sock",
+ ));
+ });
+
+ thread::spawn(move || {
+ Runtime::new().unwrap().block_on(start_socket_server(
+ upgrade_status_sender,
+ "/tmp/pika_apt_upgrade_status.sock",
+ &log_file_path,
+ ));
+ });
+
+ thread::spawn(move || {
+ let apt_upgrade_command = Command::new("pkexec")
+ .args([
+ "/home/ward/RustroverProjects/pkg-pikman-update-manager/target/debug/apt_full_upgrade",
+ ])
+ .status()
+ .unwrap();
+ match apt_upgrade_command.code().unwrap() {
+ 0 => upgrade_status_sender_clone0
+ .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned())
+ .unwrap(),
+ 53 => {}
+ _ => {
+ upgrade_status_sender_clone0
+ .send_blocking(t!("upgrade_status_error_perms").to_string())
+ .unwrap();
+ upgrade_status_sender_clone0
+ .send_blocking("FN_OVERRIDE_FAILED".to_owned())
+ .unwrap()
+ }
+ }
+ });
+
+ let apt_upgrade_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
+
+ let apt_upgrade_dialog_progress_bar =
+ ProgressBar::builder().show_text(true).hexpand(true).build();
+
+ let apt_upgrade_dialog_spinner = Spinner::builder()
+ .hexpand(true)
+ .valign(Align::Start)
+ .halign(Align::Center)
+ .spinning(true)
+ .height_request(128)
+ .width_request(128)
+ .build();
+
+ apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_spinner);
+ apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_progress_bar);
+
+ let apt_upgrade_dialog = adw::MessageDialog::builder()
+ .transient_for(&window)
+ .extra_child(&apt_upgrade_dialog_child_box)
+ .heading(t!("apt_upgrade_dialog_heading"))
+ .width_request(500)
+ .build();
+
+ apt_upgrade_dialog.add_response(
+ "apt_upgrade_dialog_ok",
+ &t!("apt_upgrade_dialog_ok_label").to_string(),
+ );
+
+ let apt_upgrade_dialog_child_box_done =
+ Box::builder().orientation(Orientation::Vertical).build();
+
+ let apt_upgrade_log_image = Image::builder()
+ .pixel_size(128)
+ .halign(Align::Center)
+ .build();
+
+ let apt_upgrade_log_button = Button::builder()
+ .label(t!("apt_upgrade_dialog_open_log_file_label"))
+ .halign(Align::Center)
+ .margin_start(15)
+ .margin_end(15)
+ .margin_top(15)
+ .margin_bottom(15)
+ .build();
+
+ apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_image);
+ apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_button);
+
+ apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", false);
+ apt_upgrade_dialog.set_close_response("apt_upgrade_dialog_ok");
+
+ let upgrade_percent_server_context = MainContext::default();
+ // The main loop executes the asynchronous block
+ upgrade_percent_server_context.spawn_local(clone!(
+ #[weak]
+ apt_upgrade_dialog_progress_bar,
+ async move {
+ while let Ok(state) = upgrade_percent_receiver.recv().await {
+ match state.as_ref() {
+ "FN_OVERRIDE_SUCCESSFUL" => {}
+ _ => match state.parse::() {
+ Ok(p) => apt_upgrade_dialog_progress_bar.set_fraction(p / 100.0),
+ Err(_) => {}
+ },
+ }
+ }
+ }
+ ));
+
+ let upgrade_status_server_context = MainContext::default();
+ // The main loop executes the asynchronous block
+ upgrade_status_server_context.spawn_local(clone!(
+ #[weak]
+ apt_upgrade_dialog,
+ #[weak]
+ apt_upgrade_dialog_child_box,
+ #[strong]
+ apt_upgrade_dialog_child_box_done,
+ #[strong]
+ apt_upgrade_log_image,
+ async move {
+ while let Ok(state) = upgrade_status_receiver.recv().await {
+ match state.as_ref() {
+ "FN_OVERRIDE_SUCCESSFUL" => {
+ apt_upgrade_dialog_child_box.set_visible(false);
+ apt_upgrade_log_image.set_icon_name(Some("face-cool-symbolic"));
+ apt_upgrade_dialog
+ .set_extra_child(Some(&apt_upgrade_dialog_child_box_done));
+ apt_upgrade_dialog.set_title(Some(
+ &t!("apt_upgrade_dialog_status_successful").to_string(),
+ ));
+ apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true);
+ }
+ "FN_OVERRIDE_FAILED" => {
+ apt_upgrade_dialog_child_box.set_visible(false);
+ apt_upgrade_log_image.set_icon_name(Some("dialog-error-symbolic"));
+ apt_upgrade_dialog
+ .set_extra_child(Some(&apt_upgrade_dialog_child_box_done));
+ apt_upgrade_dialog
+ .set_title(Some(&t!("apt_upgrade_dialog_status_failed").to_string()));
+ apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true);
+ apt_upgrade_dialog
+ .set_response_enabled("apt_upgrade_dialog_open_log_file", true);
+ }
+ _ => apt_upgrade_dialog.set_body(&state),
+ }
+ }
+ }
+ ));
+
+ let retry_signal_action0 = retry_signal_action.clone();
+
+ apt_upgrade_log_button.connect_clicked(move |_| {
+ let _ = Command::new("xdg-open")
+ .arg(log_file_path_clone0.to_owned())
+ .spawn();
+ });
+
+ apt_upgrade_dialog.choose(None::<&gio::Cancellable>, move |choice| {
+ match choice.as_str() {
+ "apt_upgrade_dialog_ok" => {
+ retry_signal_action0.activate(None);
+ }
+ _ => {}
+ }
+ });
+}
+
+fn create_color_badge(
+ label0_text: &str,
+ label1_text: &str,
+ css_style: &str,
+ group_size: &SizeGroup,
+ group_size0: &SizeGroup,
+ group_size1: &SizeGroup,
+) -> ListBox {
+ let badge_box = Box::builder().build();
+
+ let label0 = Label::builder()
+ .label(label0_text)
+ .margin_start(5)
+ .margin_end(5)
+ .margin_bottom(1)
+ .margin_top(1)
+ .valign(Align::Center)
+ .halign(Align::Center)
+ .hexpand(true)
+ .vexpand(true)
+ .build();
+ group_size0.add_widget(&label0);
+
+ let label_separator = Separator::builder().build();
+
+ let label1 = Label::builder()
+ .label(label1_text)
+ .margin_start(3)
+ .margin_end(0)
+ .margin_bottom(1)
+ .margin_top(1)
+ .valign(Align::Center)
+ .halign(Align::Center)
+ .hexpand(true)
+ .vexpand(true)
+ .build();
+ group_size1.add_widget(&label1);
+
+ label1.add_css_class(css_style);
+
+ badge_box.append(&label0);
+ badge_box.append(&label_separator);
+ badge_box.append(&label1);
+
+ let boxedlist = ListBox::builder()
+ .selection_mode(SelectionMode::None)
+ .halign(Align::Center)
+ .margin_start(10)
+ .margin_end(10)
+ .margin_bottom(10)
+ .margin_top(10)
+ .build();
+
+ boxedlist.add_css_class("boxed-list");
+ boxedlist.append(&badge_box);
+ group_size.add_widget(&boxedlist);
+ boxedlist
+}
diff --git a/src/bin/gui/main.rs b/src/bin/gui/main.rs
index 15e66e2..bbbc648 100644
--- a/src/bin/gui/main.rs
+++ b/src/bin/gui/main.rs
@@ -2,6 +2,8 @@ mod apt_package_row;
mod apt_update_page;
mod build_ui;
mod config;
+mod flatpak_ref_row;
+mod flatpak_update_page;
use crate::config::APP_ID;
use adw::prelude::*;