use std::{cell::OnceCell, collections::HashMap};
use adw::{prelude::*, subclass::window::AdwWindowImpl};
use gettextrs::pgettext;
use gtk::{
glib::{self},
subclass::{
prelude::*,
widget::{
CompositeTemplateCallbacksClass, CompositeTemplateClass,
CompositeTemplateInitializingExt, WidgetImpl,
},
},
};
use strum::IntoEnumIterator;
use crate::{
systemd::{self, data::UnitInfo, enums::CleanOption, errors::SystemdErrors},
widget::{
close_window_shortcut,
unit_control_panel::{UnitControlPanel, work_around_dialog},
},
};
use base::enums::UnitDBusLevel;
use tracing::{info, warn};
use super::CleanUnitDialog;
#[derive(Default, gtk::CompositeTemplate)]
#[template(resource = "/io/github/plrigaux/sysd-manager/clean_dialog.ui")]
pub struct CleanDialogImp {
#[template_child]
check_button_box: TemplateChild<gtk::Box>,
#[template_child]
clean_button: TemplateChild<gtk::Button>,
#[template_child]
window_title: TemplateChild<adw::WindowTitle>,
check_buttons: OnceCell<HashMap<String, gtk::CheckButton>>,
unit_control: OnceCell<UnitControlPanel>,
}
#[gtk::template_callbacks]
impl CleanDialogImp {
#[template_callback]
fn clean_button_clicked(&self, button: gtk::Button) {
let Some(map) = self.check_buttons.get() else {
return;
};
let what: Vec<String> = map
.iter()
.filter(|(_, check_button)| check_button.is_active())
.map(|(clean_option_code, _)| clean_option_code.clone())
.collect();
let lambda_out = {
let what = what.clone();
let this = self.obj().clone();
move |method: &str,
unit: Option<&UnitInfo>,
result: Result<(), SystemdErrors>,
_control: &UnitControlPanel| {
if let Err(error) = result
&& let SystemdErrors::ZAccessDenied(_, _) = error
{
let mut cmd = "sudo systemctl clean ".to_owned();
for w in what {
cmd.push_str("--what=");
cmd.push_str(&w);
cmd.push(' ');
}
cmd.push_str(&unit.expect("Unit not None").primary());
work_around_dialog(&cmd, &error, method, &this.into())
}
}
};
let lambda = move |params: Option<(UnitDBusLevel, String)>| {
if let Some((level, primary_name)) = params {
systemd::clean_unit(level, &primary_name, &what)
} else {
Err(SystemdErrors::NoUnit)
}
};
self.unit_control
.get()
.expect("unit_control not None")
.call_method(
&pgettext("clean", "Clean"),
true,
&button,
lambda,
lambda_out,
);
}
pub(crate) fn set_unit_control_panel(&self, unit_control: &UnitControlPanel) {
let _ = self.unit_control.set(unit_control.clone());
let sub_title = match unit_control.current_unit() {
Some(u) => u.primary(),
None => {
warn!("set unit to None");
pgettext("clean", "No Unit Selected")
}
};
self.window_title.set_subtitle(&sub_title);
self.set_send_button_sensitivity();
}
pub(super) fn clean_option_selected(&self, _clean_option: &CleanOption, _is_active: bool) {
self.set_send_button_sensitivity();
}
fn set_send_button_sensitivity(&self) {
if self
.unit_control
.get()
.and_then(|unit_control| unit_control.current_unit())
.is_none()
{
self.clean_button.set_sensitive(false);
return;
}
let Some(map) = self.check_buttons.get() else {
return;
};
let code_all = CleanOption::All.code();
if let Some(all) = map.get(code_all)
&& all.is_active()
{
for (key, check_button) in map.iter() {
if key != code_all {
check_button.set_active(false);
}
}
}
let mut at_least_one_checked = false;
for check_button in map.values() {
at_least_one_checked |= check_button.is_active();
}
self.clean_button.set_sensitive(at_least_one_checked);
}
}
#[glib::object_subclass]
impl ObjectSubclass for CleanDialogImp {
const NAME: &'static str = "CLEAN_DIALOG";
type Type = CleanUnitDialog;
type ParentType = adw::Window;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
klass.bind_template_callbacks();
}
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for CleanDialogImp {
fn constructed(&self) {
self.parent_constructed();
close_window_shortcut(self.obj().as_ref());
let mut check_buttons = HashMap::new();
for clean_option in CleanOption::iter() {
let check_button = gtk::CheckButton::builder()
.label(clean_option.label())
.use_underline(true)
.build();
let clean_dialog = self.obj().clone();
check_button.connect_active_notify(move |check_button| {
info!(
"{} is active {}",
clean_option.code(),
check_button.is_active()
);
clean_dialog
.imp()
.clean_option_selected(&clean_option, check_button.is_active());
});
self.check_button_box.append(&check_button);
check_buttons.insert(clean_option.code().to_owned(), check_button);
}
self.check_buttons
.set(check_buttons)
.expect("check_buttons set once");
}
}
impl WidgetImpl for CleanDialogImp {}
impl WindowImpl for CleanDialogImp {}
impl AdwWindowImpl for CleanDialogImp {}