diff --git a/po/meson.build b/po/meson.build index 57d1266..bfc35fd 100644 --- a/po/meson.build +++ b/po/meson.build @@ -1 +1,7 @@ -i18n.gettext(gettext_package, preset: 'glib') +i18n.gettext( + gettext_package, + args: [ + '--keyword=i18n_f' + ], + preset: 'glib' +) diff --git a/src/meson.build b/src/meson.build index 361762e..37eaa4f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,7 @@ sources = files( 'models/algorithm.rs', 'models/database.rs', 'models/favicon.rs', + 'models/i18n.rs', 'models/keyring.rs', 'models/mod.rs', 'models/otp.rs', diff --git a/src/models/i18n.rs b/src/models/i18n.rs new file mode 100644 index 0000000..1017215 --- /dev/null +++ b/src/models/i18n.rs @@ -0,0 +1,15 @@ +use gettextrs::gettext; + +fn freplace(input: String, args: &[&str]) -> String { + let mut parts = input.split("{}"); + let mut output = parts.next().unwrap_or("").to_string(); + for (p, a) in parts.zip(args.iter()) { + output += &(a.to_string() + &p.to_string()); + } + output +} + +pub(crate) fn i18n_f(format: &str, args: &[&str]) -> String { + let s = gettext(format); + freplace(s, args) +} diff --git a/src/models/mod.rs b/src/models/mod.rs index f325e31..3267912 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -5,6 +5,7 @@ mod accounts; mod algorithm; pub mod database; mod favicon; +pub mod i18n; mod keyring; pub mod otp; mod otp_uri; diff --git a/src/widgets/accounts/add.rs b/src/widgets/accounts/add.rs index 334ee53..09a2f0d 100644 --- a/src/widgets/accounts/add.rs +++ b/src/widgets/accounts/add.rs @@ -187,7 +187,7 @@ impl AccountAddDialog { otp_uri.digits, otp_uri.counter, ) - .unwrap(); + .ok(); self.set_provider(provider); } @@ -214,43 +214,47 @@ impl AccountAddDialog { Ok(()) } - fn set_provider(&self, provider: Provider) { + fn set_provider(&self, provider: Option) { let self_ = imp::AccountAddDialog::from_instance(self); - self_.more_list.show(); - self_.provider_entry.set_text(&provider.name()); - self_.period_label.set_text(&provider.period().to_string()); + if let Some(provider) = provider { + self_.more_list.show(); + self_.provider_entry.set_text(&provider.name()); + self_.period_label.set_text(&provider.period().to_string()); - self_.image.set_provider(&provider); + self_.image.set_provider(Some(&provider)); - self_ - .method_label - .set_text(&provider.method().to_locale_string()); + self_ + .method_label + .set_text(&provider.method().to_locale_string()); - self_ - .algorithm_label - .set_text(&provider.algorithm().to_locale_string()); + self_ + .algorithm_label + .set_text(&provider.algorithm().to_locale_string()); - self_.digits_label.set_text(&provider.digits().to_string()); + self_.digits_label.set_text(&provider.digits().to_string()); - match provider.method() { - OTPMethod::TOTP => { - self_.counter_row.hide(); - self_.period_row.show(); + match provider.method() { + OTPMethod::TOTP => { + self_.counter_row.hide(); + self_.period_row.show(); + } + OTPMethod::HOTP => { + self_.counter_row.show(); + self_.period_row.hide(); + } + OTPMethod::Steam => {} + }; + + if let Some(ref website) = provider.website() { + self_.provider_website_row.set_uri(website); } - OTPMethod::HOTP => { - self_.counter_row.show(); - self_.period_row.hide(); + if let Some(ref help_url) = provider.help_url() { + self_.provider_help_row.set_uri(help_url); } - OTPMethod::Steam => {} - }; - - if let Some(ref website) = provider.website() { - self_.provider_website_row.set_uri(website); + self_.selected_provider.borrow_mut().replace(provider); + } else { + self_.selected_provider.borrow_mut().take(); } - if let Some(ref help_url) = provider.help_url() { - self_.provider_help_row.set_uri(help_url); - } - self_.selected_provider.borrow_mut().replace(provider); } fn setup_actions(&self) { @@ -300,7 +304,7 @@ impl AccountAddDialog { self_.provider_completion.connect_match_selected( clone!(@weak self as dialog, @strong self_.model as model => move |_, store, iter| { let provider_id = store.get_value(iter, 0).get_some::().unwrap(); - let provider = model.get().unwrap().find_by_id(provider_id).unwrap(); + let provider = model.get().unwrap().find_by_id(provider_id); dialog.set_provider(provider); Inhibit(false) diff --git a/src/widgets/providers/dialog.rs b/src/widgets/providers/dialog.rs index 0bda737..d72b25e 100644 --- a/src/widgets/providers/dialog.rs +++ b/src/widgets/providers/dialog.rs @@ -1,4 +1,4 @@ -use super::{ProviderPage, ProviderPageMode}; +use super::ProviderPage; use crate::models::{Provider, ProviderSorter, ProvidersModel}; use glib::clone; use gtk::{gio, glib, prelude::*, subclass::prelude::*, CompositeTemplate}; @@ -186,14 +186,14 @@ impl ProvidersDialog { fn add_provider(&self) { let self_ = imp::ProvidersDialog::from_instance(self); self_.deck.set_visible_child_name("provider"); - self_.page.set_mode(ProviderPageMode::Create); + // By not setting the current provider we implicitly say it's for creating a new one + self_.page.set_provider(None); } fn edit_provider(&self, provider: Provider) { let self_ = imp::ProvidersDialog::from_instance(self); self_.deck.set_visible_child_name("provider"); - self_.page.set_provider(provider); - self_.page.set_mode(ProviderPageMode::Edit); + self_.page.set_provider(Some(provider)); } } diff --git a/src/widgets/providers/image.rs b/src/widgets/providers/image.rs index 4d17a4b..54c5e8a 100644 --- a/src/widgets/providers/image.rs +++ b/src/widgets/providers/image.rs @@ -132,37 +132,43 @@ impl ProviderImage { glib::Object::new(&[]).expect("Failed to create ProviderImage") } - pub fn set_provider(&self, provider: &Provider) { + pub fn set_provider(&self, provider: Option<&Provider>) { let self_ = imp::ProviderImage::from_instance(self); - self_.stack.set_visible_child_name("loading"); - self_.spinner.start(); + if let Some(provider) = provider { + self_.stack.set_visible_child_name("loading"); + self_.spinner.start(); - self.set_property("provider", &provider.clone()).unwrap(); + self.set_property("provider", &provider.clone()).unwrap(); - match provider.image_uri() { - Some(uri) => { - // Very dirty hack to store that we couldn't find an icon - // to avoid re-hitting the website every time we have to display it - if uri == "invalid" { - self_ - .image - .set_from_icon_name(Some("image-missing-symbolic")); + match provider.image_uri() { + Some(uri) => { + // Very dirty hack to store that we couldn't find an icon + // to avoid re-hitting the website every time we have to display it + if uri == "invalid" { + self_ + .image + .set_from_icon_name(Some("image-missing-symbolic")); + self_.stack.set_visible_child_name("image"); + return; + } + + let file = gio::File::new_for_uri(&uri); + if !file.query_exists(gio::NONE_CANCELLABLE) { + self.fetch(); + return; + } + + self_.image.set_from_file(file.get_path().unwrap()); self_.stack.set_visible_child_name("image"); - return; } - - let file = gio::File::new_for_uri(&uri); - if !file.query_exists(gio::NONE_CANCELLABLE) { + _ => { self.fetch(); - return; } - - self_.image.set_from_file(file.get_path().unwrap()); - self_.stack.set_visible_child_name("image"); - } - _ => { - self.fetch(); } + } else { + self_ + .image + .set_from_icon_name(Some("image-missing-symbolic")); } } diff --git a/src/widgets/providers/mod.rs b/src/widgets/providers/mod.rs index 408b37b..309b342 100644 --- a/src/widgets/providers/mod.rs +++ b/src/widgets/providers/mod.rs @@ -7,6 +7,6 @@ pub use self::{ dialog::ProvidersDialog, image::ProviderImage, list::{ProvidersList, ProvidersListView}, - page::{ProviderPage, ProviderPageMode}, + page::ProviderPage, row::ProviderRow, }; diff --git a/src/widgets/providers/page.rs b/src/widgets/providers/page.rs index 5d48c4c..66a414c 100644 --- a/src/widgets/providers/page.rs +++ b/src/widgets/providers/page.rs @@ -1,5 +1,5 @@ use crate::{ - models::{otp, Algorithm, OTPMethod, Provider}, + models::{i18n, otp, Algorithm, OTPMethod, Provider}, widgets::ProviderImage, }; use adw::ComboRowExt; @@ -7,11 +7,6 @@ use gettextrs::gettext; use glib::{clone, translate::ToGlib}; use gtk::{glib, prelude::*, subclass::prelude::*, CompositeTemplate}; -pub enum ProviderPageMode { - Create, - Edit, -} - mod imp { use crate::models::OTPMethod; @@ -112,40 +107,69 @@ impl ProviderPage { glib::Object::new(&[]).expect("Failed to create ProviderPage") } - pub fn set_provider(&self, provider: Provider) { + pub fn set_provider(&self, provider: Option) { let self_ = imp::ProviderPage::from_instance(self); - self_.name_entry.set_text(&provider.name()); - self_.period_spinbutton.set_value(provider.period() as f64); + if let Some(provider) = provider { + self_.name_entry.set_text(&provider.name()); + self_.period_spinbutton.set_value(provider.period() as f64); - if let Some(ref website) = provider.website() { - self_.provider_website_entry.set_text(website); - } + if let Some(ref website) = provider.website() { + self_.provider_website_entry.set_text(website); + } - if let Some(ref website) = provider.help_url() { - self_.provider_help_entry.set_text(website); - } + if let Some(ref website) = provider.help_url() { + self_.provider_help_entry.set_text(website); + } + + self_.algorithm_comborow.set_selected( + self_ + .algorithms_model + .find_position(provider.algorithm().to_glib()), + ); - self_.algorithm_comborow.set_selected( self_ - .algorithms_model - .find_position(provider.algorithm().to_glib()), - ); - self.on_method_changed(); + .default_counter_spinbutton + .set_value(provider.default_counter() as f64); + self_.digits_spinbutton.set_value(provider.digits() as f64); - self_ - .default_counter_spinbutton - .set_value(provider.default_counter() as f64); - self_.digits_spinbutton.set_value(provider.digits() as f64); - - self_.method_comborow.set_selected( + self_.method_comborow.set_selected( + self_ + .methods_model + .find_position(provider.method().to_glib()), + ); + self_.image.set_provider(Some(&provider)); self_ - .methods_model - .find_position(provider.method().to_glib()), - ); - self_.image.set_provider(&provider); - self_ - .title - .set_text(&format!("Editing provider: {}", provider.name())); + .title + .set_text(&i18n::i18n_f("Editing Provider: {}", &[&provider.name()])); + } else { + self_.name_entry.set_text(""); + self_ + .period_spinbutton + .set_value(otp::TOTP_DEFAULT_PERIOD as f64); + self_.provider_website_entry.set_text(""); + self_.provider_help_entry.set_text(""); + + self_.algorithm_comborow.set_selected( + self_ + .algorithms_model + .find_position(Algorithm::default().to_glib()), + ); + + self_ + .default_counter_spinbutton + .set_value(otp::HOTP_DEFAULT_COUNTER as f64); + self_ + .digits_spinbutton + .set_value(otp::DEFAULT_DIGITS as f64); + + self_.method_comborow.set_selected( + self_ + .methods_model + .find_position(OTPMethod::default().to_glib()), + ); + self_.image.set_provider(None); + self_.title.set_text(&gettext("New Provider")); + } } fn setup_widgets(&self) { @@ -178,22 +202,4 @@ impl ProviderPage { OTPMethod::Steam => {} } } - - pub fn set_mode(&self, mode: ProviderPageMode) { - let self_ = imp::ProviderPage::from_instance(self); - match mode { - ProviderPageMode::Create => { - self_.title.set_label(&gettext("New Provider")); - self_.name_entry.set_text(""); - self_ - .period_spinbutton - .set_value(otp::TOTP_DEFAULT_PERIOD as f64); - self_.provider_website_entry.set_text(""); - self_.provider_help_entry.set_text(""); - - self_.method_comborow.set_selected(0); - } - ProviderPageMode::Edit => {} - } - } } diff --git a/src/widgets/providers/row.rs b/src/widgets/providers/row.rs index 9cc02f0..7845b13 100644 --- a/src/widgets/providers/row.rs +++ b/src/widgets/providers/row.rs @@ -232,7 +232,7 @@ impl ProviderRow { self.add_css_class(&self.provider().method().to_string()); - self_.image.set_provider(&self.provider()); + self_.image.set_provider(Some(&self.provider())); self.restart(); if self.provider().method() == OTPMethod::TOTP {