diff --git a/src/locale.rs b/src/locale.rs index de5df977..6b87f68c 100644 --- a/src/locale.rs +++ b/src/locale.rs @@ -1,4 +1,10 @@ -/*! Locale-specific functions. */ +/*! Locale-specific functions. + * + * This file is intended as a library: + * it must pass errors upwards + * and panicking is allowed only when + * this code encounters an internal inconsistency. + */ use std::cmp; use std::ffi::{ CStr, CString }; diff --git a/src/logging.rs b/src/logging.rs index 9848783e..7beb9ff1 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -70,12 +70,12 @@ pub enum Level { /// Approach 2. pub trait Warn { type Value; - fn ok_warn(self, msg: &str) -> Option; + fn or_warn(self, msg: &str) -> Option; } impl Warn for Result { type Value = T; - fn ok_warn(self, msg: &str) -> Option { + fn or_warn(self, msg: &str) -> Option { self.map_err(|e| { eprintln!("{}: {}", msg, e); e @@ -83,6 +83,16 @@ impl Warn for Result { } } +impl Warn for Option { + type Value = T; + fn or_warn(self, msg: &str) -> Option { + self.or_else(|| { + eprintln!("{}", msg); + None + }) + } +} + /// A mutable handler for text warnings. /// Approach 3. pub trait WarningHandler { diff --git a/src/popover.rs b/src/popover.rs index ee99c1c8..c0b53086 100644 --- a/src/popover.rs +++ b/src/popover.rs @@ -18,6 +18,7 @@ use glib::variant::ToVariant; use gtk::PopoverExt; use gtk::WidgetExt; use std::io::Write; +use ::logging::Warn; mod variants { use glib; @@ -195,6 +196,82 @@ fn get_current_layout( } } +/// Translates all provided layout names according to current locale, +/// for the purpose of display (i.e. errors will be caught and reported) +fn translate_layout_names(layouts: &Vec) -> Vec { + // This procedure is rather ugly... + // Xkb lookup *must not* be applied to non-system layouts, + // so both translators can't be merged into one lookup table, + // therefore must be done in two steps. + // `XkbInfo` being temporary also means + // that its return values must be copied, + // forcing the use of `OwnedTranslation`. + enum Status { + /// xkb names should get all translated here + Translated(OwnedTranslation), + /// Builtin names need builtin translations + Remaining(String), + } + + // Attempt to take all xkb names from gnome-desktop's xkb info. + let xkb_translator = locale::XkbInfo::new(); + + let translated_names = layouts.iter() + .map(|id| match id { + LayoutId::System { name, kind: _ } => { + xkb_translator.get_display_name(name) + .map(|s| Status::Translated(OwnedTranslation(s))) + .unwrap_or_else(|e| { + eprintln!( + "No display name for xkb layout {}: {:?}", + name, + e, + ); + Status::Remaining(name.clone()) + }) + }, + LayoutId::Local(name) => Status::Remaining(name.clone()), + }); + + // Non-xkb layouts and weird xkb layouts + // still need to be looked up in the internal database. + let builtin_translations = system_locale() + .map(|locale| + locale.tags_for("messages") + .next().unwrap() // guaranteed to exist + .as_ref() + .to_owned() + ) + .or_warn("No locale detected") + .and_then(|lang| { + resources::get_layout_names(lang.as_str()) + .or_warn(&format!("No translations for locale {}", lang)) + }); + + match builtin_translations { + Some(translations) => { + translated_names + .map(|status| match status { + Status::Remaining(name) => { + translations.get(name.as_str()) + .unwrap_or(&Translation(name.as_str())) + .to_owned() + }, + Status::Translated(t) => t, + }) + .collect() + }, + None => { + translated_names + .map(|status| match status { + Status::Remaining(name) => OwnedTranslation(name), + Status::Translated(t) => t, + }) + .collect() + }, + } +} + pub fn show( window: EekGtkKeyboard, position: Bounds, @@ -219,69 +296,8 @@ pub fn show( .chain(overlay_layouts) .collect(); - let translations = system_locale() - .map(|locale| - locale.tags_for("messages") - .next().unwrap() // guaranteed to exist - .as_ref() - .to_owned() - ) - .and_then(|lang| resources::get_layout_names(lang.as_str())); - - // The actual translation procedure attempts to take all xkb names - // from gnome-desktop's xkb info. - // Remaining names are translated using the internal database, - // which is only available if the locale is set. - // The result is a rather ugly and verbose translation procedure... - enum Status { - /// xkb names should get all translated here - Translated(OwnedTranslation), - /// Builtin names need builtin translations - Remaining(String), - } + let translated_names = translate_layout_names(&all_layouts); - let xkb_translator = locale::XkbInfo::new(); - - let translated_names = all_layouts.iter() - .map(|id| match id { - LayoutId::System { name, kind: _ } => { - xkb_translator.get_display_name(name) - .map(|s| Status::Translated(OwnedTranslation(s))) - .unwrap_or_else(|e| { - eprintln!( - "No display name for xkb layout {}: {:?}", - name, - e, - ); - Status::Remaining(name.clone()) - }) - }, - LayoutId::Local(name) => Status::Remaining(name.clone()), - }); - - let translated_names: Vec = match translations { - Some(translations) => { - translated_names - .map(|status| match status { - Status::Remaining(name) => { - translations.get(name.as_str()) - .unwrap_or(&Translation(name.as_str())) - .to_owned() - }, - Status::Translated(t) => t, - }) - .collect() - }, - None => { - translated_names - .map(|status| match status { - Status::Remaining(name) => OwnedTranslation(name), - Status::Translated(t) => t, - }) - .collect() - }, - }; - // sorted collection of human and machine names let mut human_names: Vec<(OwnedTranslation, LayoutId)> = translated_names .into_iter() diff --git a/src/style.rs b/src/style.rs index b12e756c..381dd045 100644 --- a/src/style.rs +++ b/src/style.rs @@ -16,7 +16,7 @@ * License along with this library. If not, see .Free */ -/*! CSS data loading */ +/*! CSS data loading. */ use std::env; @@ -83,6 +83,7 @@ fn get_theme_name(settings: >k::Settings) -> GtkTheme { .map_err(|e| { match &e { env::VarError::NotPresent => {}, + // maybe TODO: forward this warning? e => eprintln!("GTK_THEME variable invalid: {}", e), }; e @@ -93,13 +94,15 @@ fn get_theme_name(settings: >k::Settings) -> GtkTheme { None => GtkTheme { name: { settings.get_property("gtk-theme-name") - .ok_warn("No theme name") + // maybe TODO: is this worth a warning? + .or_warn("No theme name") .and_then(|value| value.get::()) .unwrap_or(DEFAULT_THEME_NAME.into()) }, variant: { settings.get_property("gtk-application-prefer-dark-theme") - .ok_warn("No settings key") + // maybe TODO: is this worth a warning? + .or_warn("No settings key") .and_then(|value| value.get::()) .and_then(|dark_preferred| match dark_preferred { true => Some("dark".into()), diff --git a/src/util.rs b/src/util.rs index 55a4373f..d58a3e77 100644 --- a/src/util.rs +++ b/src/util.rs @@ -190,6 +190,11 @@ impl Borrow> for Pointer { } } +pub trait WarningHandler { + /// Handle a warning + fn handle(&mut self, warning: &str); +} + #[cfg(test)] mod tests { use super::*;