diff --git a/data/langs/bg-BG.txt b/data/langs/bg-BG.txt
deleted file mode 100644
index ae158a07..00000000
--- a/data/langs/bg-BG.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-ara Арабски
-be Белгийски
-bg Български
-br Бразилски
-cz Чешки
-de Немски
-dk Датски
-es Испански
-emoji Емоджи
-fi Фински
-fr Френски
-gr Гръцки
-it Италиански
-jp Японски
-no Норвежки
-pl Полски
-ru Руски
-se Шведски
-th Тайски
-ua Украински
-terminal Терминал
-us Английски (САЩ)
diff --git a/data/langs/cs-CZ.txt b/data/langs/cs-CZ.txt
deleted file mode 100644
index 6c87837e..00000000
--- a/data/langs/cs-CZ.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-be Belgická
-cz Česká
-cz+qwerty Česká (QWERTY)
-de Německá
-dk Dánská
-emoji Emoji
-es Španělská
-fi Finská
-fr Francouzská
-gr Řecká
-it Italská
-jp Japonská
-jp+kana Japonská (Kana)
-no Norská
-pl Polská
-ru Ruská
-se Švédská
-terminal Terminál
-th Thajská
-ua Ukrajinská
-us Anglická (USA)
diff --git a/data/langs/de-DE.txt b/data/langs/de-DE.txt
deleted file mode 100644
index e89678c3..00000000
--- a/data/langs/de-DE.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-emoji Emoji
-terminal Terminal
diff --git a/data/langs/en-US.txt b/data/langs/en-US.txt
deleted file mode 100644
index e89678c3..00000000
--- a/data/langs/en-US.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-emoji Emoji
-terminal Terminal
diff --git a/data/langs/es-ES.txt b/data/langs/es-ES.txt
deleted file mode 100644
index d377de21..00000000
--- a/data/langs/es-ES.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-us Inglés (EE.UU.)
-de Alemán
-el Griego
-es Español
-it Italiano
-jp+kana Japonés (Kana)
-no Noruego
diff --git a/data/langs/fa-IR.txt b/data/langs/fa-IR.txt
deleted file mode 100644
index 17a65aec..00000000
--- a/data/langs/fa-IR.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-emoji ایموجی
-terminal ترمینال
diff --git a/data/langs/fur-IT.txt b/data/langs/fur-IT.txt
deleted file mode 100644
index 05b78268..00000000
--- a/data/langs/fur-IT.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-be Belgjic
-br Brasilian
-de Todesc
-dk Danês
-es Spagnûl
-fi Finlandês
-fr Francês
-it+fur Furlan
-gr Grêc
-it Talian
-jp+kana Gjaponês (Kana)
-no Norvegjês
-pl Polac
-ru Rus
-se Svedês
-terminal Terminâl
-ua Ucrain
-us American (USA)
diff --git a/data/langs/he-IL.txt b/data/langs/he-IL.txt
deleted file mode 100644
index 221cdf77..00000000
--- a/data/langs/he-IL.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-be בלגית
-br פורטוגזית (ברזיל)
-cz צ'כית
-de גרמנית
-dk דנית
-es ספרדית
-emoji אימוג'י
-fi פינית
-fr צרפתית
-gr יוונית
-il עברית
-it איטלקית
-no נורווגית
-pl פולנית
-ru רוסית
-se שוודית
-terminal טרמינל
-ua אוקראינית
-us אנגלית (ארה"ב)
diff --git a/data/langs/hy-AM.txt b/data/langs/hy-AM.txt
deleted file mode 100644
index a181b1dd..00000000
--- a/data/langs/hy-AM.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-de Գերմաներէն
-es Իսպաներէն
-fi Ֆիններէն
-gr Յունարէն
-it Իտալերէն
-no Նորուեգերէն
-pl Լեհերէն
-ru Ռուսերէն
-se Շուեդերէն
-terminal Տերմինալ
-us Անգլերէն (ԱՄՆ)
diff --git a/data/langs/ja-JP.txt b/data/langs/ja-JP.txt
deleted file mode 100644
index e69de29b..00000000
diff --git a/data/langs/pl-PL.txt b/data/langs/pl-PL.txt
deleted file mode 100644
index 0884b6fb..00000000
--- a/data/langs/pl-PL.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-emoji emoji
-terminal terminal
diff --git a/data/langs/ru-RU.txt b/data/langs/ru-RU.txt
deleted file mode 100644
index 23b131e0..00000000
--- a/data/langs/ru-RU.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-de Немецкий
-es Испанский
-fi Финский
-gr Греческий
-it Итальянский
-no Норвежский
-pl Польский
-ru Русский
-se Шведский
-terminal Терминал
-us Английский (США)
diff --git a/data/popover.ui b/data/popover.ui
new file mode 100644
index 00000000..b02e71c0
--- /dev/null
+++ b/data/popover.ui
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/data/popup.ui b/data/popup.ui
deleted file mode 100644
index 214fbea6..00000000
--- a/data/popup.ui
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/data/squeekboard.gresources.xml b/data/squeekboard.gresources.xml
index 795d22b7..6c5d0213 100644
--- a/data/squeekboard.gresources.xml
+++ b/data/squeekboard.gresources.xml
@@ -4,7 +4,7 @@
common.css
style.css
style-Adwaita:dark.css
- popup.ui
+ popover.ui
icons/key-enter.svg
icons/key-shift.svg
icons/keyboard-mode-symbolic.svg
diff --git a/src/lib.rs b/src/lib.rs
index 287b5286..a133e9ee 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,7 +26,6 @@ pub mod imservice;
mod keyboard;
mod layout;
mod locale;
-mod locale_config;
mod manager;
mod outputs;
mod popover;
diff --git a/src/locale.rs b/src/locale.rs
index 63ff6fca..8bdae9ba 100644
--- a/src/locale.rs
+++ b/src/locale.rs
@@ -88,15 +88,6 @@ impl Drop for XkbInfo {
}
}
-#[derive(Clone, Debug, PartialEq)]
-pub struct Translation<'a>(pub &'a str);
-
-impl<'a> Translation<'a> {
- pub fn to_owned(&'a self) -> OwnedTranslation {
- OwnedTranslation(self.0.to_owned())
- }
-}
-
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedTranslation(pub String);
diff --git a/src/locale_config.rs b/src/locale_config.rs
deleted file mode 100644
index 959dfde1..00000000
--- a/src/locale_config.rs
+++ /dev/null
@@ -1,535 +0,0 @@
-/*! Locale detection and management.
- * Based on https://github.com/rust-locale/locale_config
- *
- * Ready for deletion/replacement once Debian starts packaging this,
- * although this version doesn't need lazy_static.
- *
- * Copyright (c) 2016–2019 Jan Hudec
- Copyright (c) 2016 A.J. Gardner
- Copyright (c) 2019, Bastien Orivel
- Copyright (c) 2019, Igor Gnatenko
- Copyright (c) 2019, Sophie Tauchert <999eagle@999eagle.moe>
- */
-
-use regex::Regex;
-use std::borrow::Cow;
-use std::env;
-
-/// Errors that may be returned by `locale_config`.
-#[derive(Copy,Clone,Debug,PartialEq,Eq)]
-pub enum Error {
- /// Provided definition was not well formed.
- ///
- /// This is returned when provided configuration string does not match even the rather loose
- /// definition for language range from [RFC4647] or the composition format used by `Locale`.
- ///
- /// [RFC4647]: https://www.rfc-editor.org/rfc/rfc4647.txt
- NotWellFormed,
- /// Placeholder for adding more errors in future. **Do not match!**.
- __NonExhaustive,
-}
-
-impl ::std::fmt::Display for Error {
- fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
- out.write_str(match self {
- &Error::NotWellFormed => "Language tag is not well-formed.",
- // this is exception: here we do want exhaustive match so we don't publish version with
- // missing descriptions by mistake.
- &Error::__NonExhaustive => panic!("Placeholder error must not be instantiated!"),
- })
- }
-}
-
-
-/// Convenience Result alias.
-type Result = ::std::result::Result;
-
-/// Iterator over `LanguageRange`s for specific category in a `Locale`
-///
-/// Returns `LanguageRange`s in the `Locale` that are applicable to provided category. The tags
-/// are returned in order of preference, which means the category-specific ones first and then
-/// the generic ones.
-///
-/// The iterator is guaranteed to return at least one value.
-pub struct TagsFor<'a, 'c> {
- src: &'a str,
- tags: std::str::Split<'a, &'static str>,
- category: Option<&'c str>,
-}
-
-impl<'a, 'c> Iterator for TagsFor<'a, 'c> {
- type Item = LanguageRange<'a>;
- fn next(&mut self) -> Option {
- if let Some(cat) = self.category {
- while let Some(s) = self.tags.next() {
- if s.starts_with(cat) && s[cat.len()..].starts_with("=") {
- return Some(
- LanguageRange { language: Cow::Borrowed(&s[cat.len()+1..]) });
- }
- }
- self.category = None;
- self.tags = self.src.split(",");
- }
- while let Some(s) = self.tags.next() {
- if s.find('=').is_none() {
- return Some(
- LanguageRange{ language: Cow::Borrowed(s) });
- }
- }
- return None;
- }
-}
-
-/// Language and culture identifier.
-///
-/// This object holds a [RFC4647] extended language range.
-///
-/// The internal data may be owned or shared from object with lifetime `'a`. The lifetime can be
-/// extended using the `into_static()` method, which internally clones the data as needed.
-///
-/// # Syntax
-///
-/// The range is composed of `-`-separated alphanumeric subtags, possibly replaced by `*`s. It
-/// might be empty.
-///
-/// In agreement with [RFC4647], this object only requires that the tag matches:
-///
-/// ```ebnf
-/// language_tag = (alpha{1,8} | "*")
-/// ("-" (alphanum{1,8} | "*"))*
-/// ```
-///
-/// The exact interpretation is up to the downstream localization provider, but it expected that
-/// it will be matched against a normalized [RFC5646] language tag, which has the structure:
-///
-/// ```ebnf
-/// language_tag = language
-/// ("-" script)?
-/// ("-" region)?
-/// ("-" variant)*
-/// ("-" extension)*
-/// ("-" private)?
-///
-/// language = alpha{2,3} ("-" alpha{3}){0,3}
-///
-/// script = aplha{4}
-///
-/// region = alpha{2}
-/// | digit{3}
-///
-/// variant = alphanum{5,8}
-/// | digit alphanum{3}
-///
-/// extension = [0-9a-wyz] ("-" alphanum{2,8})+
-///
-/// private = "x" ("-" alphanum{1,8})+
-/// ```
-///
-/// * `language` is an [ISO639] 2-letter or, where not defined, 3-letter code. A code for
-/// macro-language might be followed by code of specific dialect.
-/// * `script` is an [ISO15924] 4-letter code.
-/// * `region` is either an [ISO3166] 2-letter code or, for areas other than countries, [UN M.49]
-/// 3-digit numeric code.
-/// * `variant` is a string indicating variant of the language.
-/// * `extension` and `private` define additional options. The private part has same structure as
-/// the Unicode [`-u-` extension][u_ext]. Available options are documented for the facets that
-/// use them.
-///
-/// The values obtained by inspecting the system are normalized according to those rules.
-///
-/// The content will be case-normalized as recommended in [RFC5646] §2.1.1, namely:
-///
-/// * `language` is written in lowercase,
-/// * `script` is written with first capital,
-/// * `country` is written in uppercase and
-/// * all other subtags are written in lowercase.
-///
-/// When detecting system configuration, additional options that may be generated under the
-/// [`-u-` extension][u_ext] currently are:
-///
-/// * `cf` — Currency format (`account` for parenthesized negative values, `standard` for minus
-/// sign).
-/// * `fw` — First day of week (`mon` to `sun`).
-/// * `hc` — Hour cycle (`h12` for 1–12, `h23` for 0–23).
-/// * `ms` — Measurement system (`metric` or `ussystem`).
-/// * `nu` — Numbering system—only decimal systems are currently used.
-/// * `va` — Variant when locale is specified in Unix format and the tag after `@` does not
-/// correspond to any variant defined in [Language subtag registry].
-///
-/// And under the `-x-` extension, following options are defined:
-///
-/// * `df` — Date format:
-///
-/// * `iso`: Short date should be in ISO format of `yyyy-MM-dd`.
-///
-/// For example `-df-iso`.
-///
-/// * `dm` — Decimal separator for monetary:
-///
-/// Followed by one or more Unicode codepoints in hexadecimal. For example `-dm-002d` means to
-/// use comma.
-///
-/// * `ds` — Decimal separator for numbers:
-///
-/// Followed by one or more Unicode codepoints in hexadecimal. For example `-ds-002d` means to
-/// use comma.
-///
-/// * `gm` — Group (thousand) separator for monetary:
-///
-/// Followed by one or more Unicode codepoints in hexadecimal. For example `-dm-00a0` means to
-/// use non-breaking space.
-///
-/// * `gs` — Group (thousand) separator for numbers:
-///
-/// Followed by one or more Unicode codepoints in hexadecimal. For example `-ds-00a0` means to
-/// use non-breaking space.
-///
-/// * `ls` — List separator:
-///
-/// Followed by one or more Unicode codepoints in hexadecimal. For example, `-ds-003b` means to
-/// use a semicolon.
-///
-/// [RFC5646]: https://www.rfc-editor.org/rfc/rfc5646.txt
-/// [RFC4647]: https://www.rfc-editor.org/rfc/rfc4647.txt
-/// [ISO639]: https://en.wikipedia.org/wiki/ISO_639
-/// [ISO15924]: https://en.wikipedia.org/wiki/ISO_15924
-/// [ISO3166]: https://en.wikipedia.org/wiki/ISO_3166
-/// [UN M.49]: https://en.wikipedia.org/wiki/UN_M.49
-/// [u_ext]: http://www.unicode.org/reports/tr35/#u_Extension
-/// [Language subtag registry]: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
-#[derive(Clone,Debug,Eq,Hash,PartialEq)]
-pub struct LanguageRange<'a> {
- language: Cow<'a, str>
-}
-
-impl<'a> LanguageRange<'a> {
- /// Return LanguageRange for the invariant locale.
- ///
- /// Invariant language is identified simply by empty string.
- pub fn invariant() -> LanguageRange<'static> {
- LanguageRange { language: Cow::Borrowed("") }
- }
-
- /// Create language tag from Unix/Linux/GNU locale tag.
- ///
- /// Unix locale tags have the form
- ///
- /// > *language* [ `_` *region* ] [ `.` *encoding* ] [ `@` *variant* ]
- ///
- /// The *language* and *region* have the same format as RFC5646. *Encoding* is not relevant
- /// here, since Rust always uses Utf-8. That leaves *variant*, which is unfortunately rather
- /// free-form. So this function will translate known variants to corresponding RFC5646 subtags
- /// and represent anything else with Unicode POSIX variant (`-u-va-`) extension.
- ///
- /// Note: This function is public here for benefit of applications that may come across this
- /// kind of tags from other sources than system configuration.
- pub fn from_unix(s: &str) -> Result> {
- let unix_tag_regex = Regex::new(r"(?ix) ^
- (?P [[:alpha:]]{2,3} )
- (?: _ (?P [[:alpha:]]{2} | [[:digit:]]{3} ))?
- (?: \. (?P [0-9a-zA-Z-]{1,20} ))?
- (?: @ (?P [[:alnum:]]{1,20} ))?
- $ ").unwrap();
-
- let unix_invariant_regex = Regex::new(r"(?ix) ^
- (?: c | posix )
- (?: \. (?: [0-9a-zA-Z-]{1,20} ))?
- $ ").unwrap();
-
- if let Some(caps) = unix_tag_regex.captures(s) {
- let src_variant = caps.name("variant").map(|m| m.as_str()).unwrap_or("").to_ascii_lowercase();
- let mut res = caps.name("language").map(|m| m.as_str()).unwrap().to_ascii_lowercase();
- let region = caps.name("region").map(|m| m.as_str()).unwrap_or("");
- let mut script = "";
- let mut variant = "";
- let mut uvariant = "";
- match src_variant.as_ref() {
- // Variants seen in the wild in GNU LibC (via http://lh.2xlibre.net/) or in Debian
- // GNU/Linux Stretch system. Treatment of things not found in RFC5646 subtag registry
- // (http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry)
- // or CLDR according to notes at https://wiki.openoffice.org/wiki/LocaleMapping.
- // Dialects:
- // aa_ER@saaho - NOTE: Can't be found under that name in RFC5646 subtag registry,
- // but there is language Saho with code ssy, which is likely that thing.
- "saaho" if res == "aa" => res = String::from("ssy"),
- // Scripts:
- // @arabic
- "arabic" => script = "Arab",
- // @cyrillic
- "cyrl" => script = "Cyrl",
- "cyrillic" => script = "Cyrl",
- // @devanagari
- "devanagari" => script = "Deva",
- // @hebrew
- "hebrew" => script = "Hebr",
- // tt@iqtelif
- // Neither RFC5646 subtag registry nor CLDR knows anything about this, but as best
- // as I can tell it is Tatar name for Latin (default is Cyrillic).
- "iqtelif" => script = "Latn",
- // @Latn
- "latn" => script = "Latn",
- // @latin
- "latin" => script = "Latn",
- // en@shaw
- "shaw" => script = "Shaw",
- // Variants:
- // sr@ijekavianlatin
- "ijekavianlatin" => {
- script = "Latn";
- variant = "ijekavsk";
- },
- // sr@ije
- "ije" => variant = "ijekavsk",
- // sr@ijekavian
- "ijekavian" => variant = "ijekavsk",
- // ca@valencia
- "valencia" => variant = "valencia",
- // Currencies:
- // @euro - NOTE: We follow suite of Java and Openoffice and ignore it, because it
- // is default for all locales where it sometimes appears now, and because we use
- // explicit currency in monetary formatting anyway.
- "euro" => {},
- // Collation:
- // gez@abegede - NOTE: This is collation, but CLDR does not have any code for it,
- // so we for the moment leave it fall through as -u-va- instead of -u-co-.
- // Anything else:
- // en@boldquot, en@quot, en@piglatin - just randomish stuff
- // @cjknarrow - beware, it's gonna end up as -u-va-cjknarro due to lenght limit
- s if s.len() <= 8 => uvariant = &*s,
- s => uvariant = &s[0..8], // the subtags are limited to 8 chars, but some are longer
- };
- if script != "" {
- res.push('-');
- res.push_str(script);
- }
- if region != "" {
- res.push('-');
- res.push_str(&*region.to_ascii_uppercase());
- }
- if variant != "" {
- res.push('-');
- res.push_str(variant);
- }
- if uvariant != "" {
- res.push_str("-u-va-");
- res.push_str(uvariant);
- }
- return Ok(LanguageRange {
- language: Cow::Owned(res)
- });
- } else if unix_invariant_regex.is_match(s) {
- return Ok(LanguageRange::invariant())
- } else {
- return Err(Error::NotWellFormed);
- }
- }
-}
-
-impl<'a> AsRef for LanguageRange<'a> {
- fn as_ref(&self) -> &str {
- self.language.as_ref()
- }
-}
-
-/// Locale configuration.
-///
-/// Users may accept several languages in some order of preference and may want to use rules from
-/// different culture for some particular aspect of the program behaviour, and operating systems
-/// allow them to specify this (to various extent).
-///
-/// The `Locale` objects represent the user configuration. They contain:
-///
-/// - The primary `LanguageRange`.
-/// - Optional category-specific overrides.
-/// - Optional fallbacks in case data (usually translations) for the primary language are not
-/// available.
-///
-/// The set of categories is open-ended. The `locale` crate uses five well-known categories
-/// `messages`, `numeric`, `time`, `collate` and `monetary`, but some systems define additional
-/// ones (GNU Linux has additionally `paper`, `name`, `address`, `telephone` and `measurement`) and
-/// these are provided in the user default `Locale` and other libraries can use them.
-///
-/// `Locale` is represented by a `,`-separated sequence of tags in `LanguageRange` syntax, where
-/// all except the first one may be preceded by category name and `=` sign.
-///
-/// The first tag indicates the default locale, the tags prefixed by category names indicate
-/// _overrides_ for those categories and the remaining tags indicate fallbacks.
-///
-/// Note that a syntactically valid value of HTTP `Accept-Language` header is a valid `Locale`. Not
-/// the other way around though due to the presence of category selectors.
-// TODO: Interning
-#[derive(Clone,Debug,Eq,Hash,PartialEq)]
-pub struct Locale {
- // TODO: Intern the string for performance reasons
- // XXX: Store pre-split to LanguageTags?
- inner: String,
-}
-
-impl Locale {
- /// Construct invariant locale.
- ///
- /// Invariant locale is represented simply with empty string.
- pub fn invariant() -> Locale {
- Locale::from(LanguageRange::invariant())
- }
-
- /// Append fallback language tag.
- ///
- /// Adds fallback to the end of the list.
- pub fn add(&mut self, tag: &LanguageRange) {
- for i in self.inner.split(',') {
- if i == tag.as_ref() {
- return; // don't add duplicates
- }
- }
- self.inner.push_str(",");
- self.inner.push_str(tag.as_ref());
- }
-
- /// Append category override.
- ///
- /// Appending new override for a category that already has one will not replace the existing
- /// override. This might change in future.
- pub fn add_category(&mut self, category: &str, tag: &LanguageRange) {
- if self.inner.split(',').next().unwrap() == tag.as_ref() {
- return; // don't add useless override equal to the primary tag
- }
- for i in self.inner.split(',') {
- if i.starts_with(category) &&
- i[category.len()..].starts_with("=") &&
- &i[category.len() + 1..] == tag.as_ref() {
- return; // don't add duplicates
- }
- }
- self.inner.push_str(",");
- self.inner.push_str(category);
- self.inner.push_str("=");
- self.inner.push_str(tag.as_ref());
- }
-
- /// Iterate over `LanguageRange`s in this `Locale` applicable to given category.
- ///
- /// Returns `LanguageRange`s in the `Locale` that are applicable to provided category. The tags
- /// are returned in order of preference, which means the category-specific ones first and then
- /// the generic ones.
- ///
- /// The iterator is guaranteed to return at least one value.
- pub fn tags_for<'a, 'c>(&'a self, category: &'c str) -> TagsFor<'a, 'c> {
- let mut tags = self.inner.split(",");
- while let Some(s) = tags.clone().next() {
- if s.starts_with(category) && s[category.len()..].starts_with("=") {
- return TagsFor {
- src: self.inner.as_ref(),
- tags: tags,
- category: Some(category),
- };
- }
- tags.next();
- }
- return TagsFor {
- src: self.inner.as_ref(),
- tags: self.inner.split(","),
- category: None,
- };
- }
-}
-
-/// Locale is specified by a string tag. This is the way to access it.
-// FIXME: Do we want to provide the full string representation? We would have it as single string
-// then.
-impl AsRef for Locale {
- fn as_ref(&self) -> &str {
- self.inner.as_ref()
- }
-}
-
-impl<'a> From> for Locale {
- fn from(t: LanguageRange<'a>) -> Locale {
- Locale {
- inner: t.language.into_owned(),
- }
- }
-}
-
-fn tag(s: &str) -> Result {
- LanguageRange::from_unix(s)
-}
-
-// TODO: Read /etc/locale.alias
-fn tag_inv(s: &str) -> LanguageRange {
- tag(s).unwrap_or(LanguageRange::invariant())
-}
-
-pub fn system_locale() -> Option {
- // LC_ALL overrides everything
- if let Ok(all) = env::var("LC_ALL") {
- if let Ok(t) = tag(all.as_ref()) {
- return Some(Locale::from(t));
- }
- }
- // LANG is default
- let mut loc =
- if let Ok(lang) = env::var("LANG") {
- Locale::from(tag_inv(lang.as_ref()))
- } else {
- Locale::invariant()
- };
- // category overrides
- for &(cat, var) in [
- ("ctype", "LC_CTYPE"),
- ("numeric", "LC_NUMERIC"),
- ("time", "LC_TIME"),
- ("collate", "LC_COLLATE"),
- ("monetary", "LC_MONETARY"),
- ("messages", "LC_MESSAGES"),
- ("paper", "LC_PAPER"),
- ("name", "LC_NAME"),
- ("address", "LC_ADDRESS"),
- ("telephone", "LC_TELEPHONE"),
- ("measurement", "LC_MEASUREMENT"),
- ].iter() {
- if let Ok(val) = env::var(var) {
- if let Ok(tag) = tag(val.as_ref())
- {
- loc.add_category(cat, &tag);
- }
- }
- }
- // LANGUAGE defines fallbacks
- if let Ok(langs) = env::var("LANGUAGE") {
- for i in langs.split(':') {
- if i != "" {
- if let Ok(tag) = tag(i) {
- loc.add(&tag);
- }
- }
- }
- }
- if loc.as_ref() != "" {
- return Some(loc);
- } else {
- return None;
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::LanguageRange;
-
- #[test]
- fn unix_tags() {
- assert_eq!("cs-CZ", LanguageRange::from_unix("cs_CZ.UTF-8").unwrap().as_ref());
- assert_eq!("sr-RS-ijekavsk", LanguageRange::from_unix("sr_RS@ijekavian").unwrap().as_ref());
- assert_eq!("sr-Latn-ijekavsk", LanguageRange::from_unix("sr.UTF-8@ijekavianlatin").unwrap().as_ref());
- assert_eq!("en-Arab", LanguageRange::from_unix("en@arabic").unwrap().as_ref());
- assert_eq!("en-Arab", LanguageRange::from_unix("en.UTF-8@arabic").unwrap().as_ref());
- assert_eq!("de-DE", LanguageRange::from_unix("DE_de.UTF-8@euro").unwrap().as_ref());
- assert_eq!("ssy-ER", LanguageRange::from_unix("aa_ER@saaho").unwrap().as_ref());
- assert!(LanguageRange::from_unix("foo_BAR").is_err());
- assert!(LanguageRange::from_unix("en@arabic.UTF-8").is_err());
- assert_eq!("", LanguageRange::from_unix("C").unwrap().as_ref());
- assert_eq!("", LanguageRange::from_unix("C.UTF-8").unwrap().as_ref());
- assert_eq!("", LanguageRange::from_unix("C.ISO-8859-1").unwrap().as_ref());
- assert_eq!("", LanguageRange::from_unix("POSIX").unwrap().as_ref());
- }
-}
diff --git a/src/popover.rs b/src/popover.rs
index 9a1b6902..feec80f1 100644
--- a/src/popover.rs
+++ b/src/popover.rs
@@ -5,9 +5,7 @@ use gtk;
use std::ffi::CString;
use std::cmp::Ordering;
use ::layout::c::{ Bounds, EekGtkKeyboard };
-use ::locale;
-use ::locale::{ OwnedTranslation, Translation, compare_current_locale };
-use ::locale_config::system_locale;
+use ::locale::{ OwnedTranslation, compare_current_locale };
use ::logging;
use ::manager;
use ::resources;
@@ -20,10 +18,9 @@ use gio::SimpleActionExt;
use glib::translate::FromGlibPtrNone;
use glib::variant::ToVariant;
#[cfg(not(feature = "gtk_v0_5"))]
-use gtk::BuilderExtManual;
+use gtk::prelude::*;
use gtk::PopoverExt;
use gtk::WidgetExt;
-use std::io::Write;
use ::logging::Warn;
mod c {
@@ -111,46 +108,6 @@ mod variants {
}
}
-fn make_menu_builder(inputs: Vec<(&str, OwnedTranslation)>) -> gtk::Builder {
- let mut xml: Vec = Vec::new();
- writeln!(
- xml,
- "
-
-
-"
- ).unwrap();
- gtk::Builder::new_from_string(
- &String::from_utf8(xml).expect("Bad menu definition")
- )
-}
-
fn get_settings(schema_name: &str) -> Option {
let mut error_handler = logging::Print{};
gio::SettingsSchemaSource::get_default()
@@ -250,13 +207,8 @@ 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`.
+ // `XkbInfo` being temporary means that its return values must be
+ // copied, forcing the use of `OwnedTranslation`.
enum Status {
/// xkb names should get all translated here
Translated(OwnedTranslation),
@@ -265,7 +217,7 @@ fn translate_layout_names(layouts: &Vec) -> Vec {
}
// Attempt to take all xkb names from gnome-desktop's xkb info.
- let xkb_translator = locale::XkbInfo::new();
+ let xkb_translator = ::locale::XkbInfo::new();
let translated_names = layouts.iter()
.map(|id| match id {
@@ -277,49 +229,15 @@ fn translate_layout_names(layouts: &Vec) -> Vec {
&format!("No display name for xkb layout {}", name),
).unwrap_or_else(|| Status::Remaining(name.clone()))
},
- LayoutId::Local(name) => Status::Remaining(name.clone()),
+ LayoutId::Local (_) => unreachable!(),
});
- // 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_print(logging::Problem::Surprise, "No locale detected")
- .and_then(|lang| {
- resources::get_layout_names(lang.as_str())
- .or_print(
- logging::Problem::Surprise,
- &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()
- },
- }
+ translated_names
+ .map(|status| match status {
+ Status::Remaining(name) => OwnedTranslation(name),
+ Status::Translated(t) => t,
+ })
+ .collect()
}
pub fn show(
@@ -350,12 +268,12 @@ pub fn show(
.chain(overlay_layouts)
.collect();
- let translated_names = translate_layout_names(&all_layouts);
-
- // sorted collection of human and machine names
+ let translated_names = translate_layout_names(&system_layouts);
+
+ // sorted collection of language layouts
let mut human_names: Vec<(OwnedTranslation, LayoutId)> = translated_names
.into_iter()
- .zip(all_layouts.clone().into_iter())
+ .zip(system_layouts.clone().into_iter())
.collect();
human_names.sort_unstable_by(|(tr_a, layout_a), (tr_b, layout_b)| {
@@ -367,32 +285,14 @@ pub fn show(
}
});
- // GVariant doesn't natively support `enum`s,
- // so the `choices` vector will serve as a lookup table.
- let choices_with_translations: Vec<(String, (OwnedTranslation, LayoutId))>
- = human_names.into_iter()
- .enumerate()
- .map(|(i, human_entry)| {(
- format!("{}_{}", i, human_entry.1.get_name()),
- human_entry,
- )}).collect();
+ let builder = gtk::Builder::new_from_resource("/sm/puri/squeekboard/popover.ui");
+ let model: gio::Menu = builder.get_object("app-menu").unwrap();
-
- let builder = make_menu_builder(
- choices_with_translations.iter()
- .map(|(id, (translation, _))| (id.as_str(), (*translation).clone()))
- .collect()
- );
-
- let choices: Vec<(String, LayoutId)>
- = choices_with_translations.into_iter()
- .map(|(id, (_tr, layout))| (id, layout))
- .collect();
-
- // Much more debuggable to populate the model & menu
- // from a string representation
- // than add items imperatively
- let model: gio::MenuModel = builder.get_object("app-menu").unwrap();
+ for (tr, l) in human_names.iter().rev() {
+ let detailed_action = format!("layout::{}", l.get_name());
+ let item = gio::MenuItem::new(Some(&tr.0), Some(detailed_action.as_str()));
+ model.prepend_item (&item);
+ }
let menu = gtk::Popover::new_from_model(Some(&window), &model);
menu.set_pointing_to(>k::Rectangle {
@@ -403,32 +303,36 @@ pub fn show(
});
menu.set_constrain_to(gtk::PopoverConstraint::None);
+ let action_group = gio::SimpleActionGroup::new();
+
if let Some(current_layout) = get_current_layout(manager, &system_layouts) {
- let current_name_variant = choices.iter()
+ let current_layout_name = all_layouts.iter()
.find(
- |(_id, layout)| layout == ¤t_layout
+ |l| l.get_name() == current_layout.get_name()
).unwrap()
- .0.to_variant();
+ .get_name();
+ log_print!(logging::Level::Debug, "Current Layout {}", current_layout_name);
let layout_action = gio::SimpleAction::new_stateful(
"layout",
- Some(current_name_variant.type_()),
- ¤t_name_variant,
+ Some(current_layout_name.to_variant().type_()),
+ ¤t_layout_name.to_variant()
);
let menu_inner = menu.clone();
layout_action.connect_change_state(move |_action, state| {
match state {
Some(v) => {
+ log_print!(logging::Level::Debug, "Selected layout {}", v);
v.get::()
.or_print(
logging::Problem::Bug,
&format!("Variant is not string: {:?}", v)
)
.map(|state| {
- let (_id, layout) = choices.iter()
+ let layout = all_layouts.iter()
.find(
- |choices| state == choices.0
+ |choices| state == choices.get_name()
).unwrap();
set_visible_layout(
manager,
@@ -443,20 +347,18 @@ pub fn show(
};
menu_inner.popdown();
});
-
- let settings_action = gio::SimpleAction::new("settings", None);
- settings_action.connect_activate(move |_, _| {
- let s = CString::new("region").unwrap();
- unsafe { c::popover_open_settings_panel(s.as_ptr()) };
- });
-
- let action_group = gio::SimpleActionGroup::new();
action_group.add_action(&layout_action);
- action_group.add_action(&settings_action);
-
- menu.insert_action_group("popup", Some(&action_group));
};
+ let settings_action = gio::SimpleAction::new("settings", None);
+ settings_action.connect_activate(move |_, _| {
+ let s = CString::new("region").unwrap();
+ unsafe { c::popover_open_settings_panel(s.as_ptr()) };
+ });
+ action_group.add_action(&settings_action);
+
+ menu.insert_action_group("popup", Some(&action_group));
+
menu.bind_model(Some(&model), Some("popup"));
menu.popup();
}
diff --git a/src/resources.rs b/src/resources.rs
index f512921d..1795dbe9 100644
--- a/src/resources.rs
+++ b/src/resources.rs
@@ -2,11 +2,6 @@
* This could be done using GResource, but that would need additional work.
*/
-use std::collections::HashMap;
-use ::locale::Translation;
-
-use std::iter::FromIterator;
-
// TODO: keep a list of what is a language layout,
// and what a convenience layout. "_wide" is not a layout,
// neither is "number"
@@ -125,46 +120,6 @@ pub fn get_overlays() -> Vec<&'static str> {
OVERLAY_NAMES.to_vec()
}
-/// Translations of the layout identifier strings
-static LAYOUT_NAMES: &[(&'static str, &'static str)] = &[
- ("de-DE", include_str!("../data/langs/de-DE.txt")),
- ("en-US", include_str!("../data/langs/en-US.txt")),
- ("es-ES", include_str!("../data/langs/es-ES.txt")),
- ("fur-IT", include_str!("../data/langs/fur-IT.txt")),
- ("he-IL", include_str!("../data/langs/he-IL.txt")),
- ("ja-JP", include_str!("../data/langs/ja-JP.txt")),
- ("pl-PL", include_str!("../data/langs/pl-PL.txt")),
- ("ru-RU", include_str!("../data/langs/ru-RU.txt")),
-];
-
-pub fn get_layout_names(lang: &str)
- -> Option>>
-{
- let translations = LAYOUT_NAMES.iter()
- .find(|(name, _data)| *name == lang)
- .map(|(_name, data)| *data);
- translations.map(make_mapping)
-}
-
-fn parse_line(line: &str) -> Option<(&str, Translation)> {
- let comment = line.trim().starts_with("#");
- if comment {
- None
- } else {
- let mut iter = line.splitn(2, " ");
- let name = iter.next().unwrap();
- // will skip empty and unfinished lines
- iter.next().map(|tr| (name, Translation(tr.trim())))
- }
-}
-
-fn make_mapping(data: &str) -> HashMap<&str, Translation> {
- HashMap::from_iter(
- data.split("\n")
- .filter_map(parse_line)
- )
-}
-
#[cfg(test)]
mod test {
use super::*;
@@ -175,32 +130,4 @@ mod test {
assert!(get_keyboard(&format!("{}/us", name)).is_some());
}
}
-
- #[test]
- fn mapping_line() {
- assert_eq!(
- Some(("name", Translation("translation"))),
- parse_line("name translation")
- );
- }
-
- #[test]
- fn mapping_bad() {
- assert_eq!(None, parse_line("bad"));
- }
-
- #[test]
- fn mapping_empty() {
- assert_eq!(None, parse_line(""));
- }
-
- #[test]
- fn mapping_comment() {
- assert_eq!(None, parse_line("# comment"));
- }
-
- #[test]
- fn mapping_comment_offset() {
- assert_eq!(None, parse_line(" # comment"));
- }
}