diff --git a/src/stages/keyboard/mod.rs b/src/stages/keyboard/mod.rs
index 7090ec2..f979f37 100644
--- a/src/stages/keyboard/mod.rs
+++ b/src/stages/keyboard/mod.rs
@@ -15,131 +15,205 @@
// along with this program. If not, see .
/*
- This is TimeZone stage, used to select timezone
+ This is Keyboar stage, used to select Keyboard Layouts
*/
+use crate::kira_scroll_list;
use crate::stage::{ConfigValue, StageAction, StageResult};
use iced::{Alignment, Length, widget};
use rust_i18n::t;
use std::collections::HashMap;
-mod scroll_list;
-
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct TimeZoneData {
- region: String,
- zone: String,
+pub struct Layout {
+ layout: String,
+ description: String,
}
-impl TimeZoneData {
- fn from_str(s: &str) -> Option {
- let idx = s.find('/')?;
- let (region, zone) = s.split_at(idx);
- Some(Self {
- region: region.into(),
- zone: zone.trim_start_matches('/').into(),
- })
+impl Layout {
+ pub fn from_str_touple(v: &(&str, &str)) -> Self {
+ Self {
+ layout: v.0.trim().to_string(),
+ description: v.1.trim().to_string(),
+ }
}
}
-impl std::fmt::Display for TimeZoneData {
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Variant {
+ variant: String,
+ description: String,
+}
+
+impl Variant {
+ pub fn from_str_touple(v: &(&str, &str)) -> Self {
+ Self {
+ variant: v.0.trim().to_string(),
+ description: v.1.trim().to_string(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct KeyboarLayout {
+ layout: Layout,
+ variant: Variant,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct KeyboarModel {
+ model: String,
+ description: String,
+}
+impl KeyboarModel {
+ pub fn from_str_touple(v: &(&str, &str)) -> Self {
+ Self {
+ model: v.0.trim().to_string(),
+ description: v.1.trim().to_string(),
+ }
+ }
+}
+
+
+impl std::fmt::Display for KeyboarLayout {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}/{}", self.region, self.zone)
+ write!(f, "{}/{}", &self.layout.layout, &self.variant.variant)
}
}
#[derive(Debug, Clone)]
-pub struct TimeZoneStage {
- time_zones: Result>, String>,
- regions: Vec,
- region_id: Option,
- zones: Vec,
- zone_id: Option,
- selected_zone: Option,
- selected_zone_text: String,
+pub struct KeyboardStage {
+ models: Vec,
+ layouts: Vec,
+ variants_map: HashMap>,
+ variants: Vec,
+ model: Option,
+ layout: Option,
+ variant: Option,
+ keyboard_layouts: Vec,
+ selected_kbd_layout_id: Option,
}
#[derive(Debug, Clone)]
pub enum Message {
- SelectRegion(scroll_list::ListViewMessage),
- SelectZone(scroll_list::ListViewMessage),
+ SelectKeyboardModel(kira_scroll_list::ListViewMessage),
+ SelectLayout(kira_scroll_list::ListViewMessage),
+ SelectVariant(kira_scroll_list::ListViewMessage),
+
+ AddLayout(usize),
+ RemoveLayout(usize),
+ SelectAddedLayout(kira_scroll_list::ListViewMessage),
+
Next,
Back,
}
-/// Getting list of awailable timezones from system.
-fn get_timezones_blocking() -> Result, String> {
- use std::process::Command;
- //timedatectl list-timezones --no-pager
- match Command::new("timedatectl")
- .arg("list-timezones")
- .arg("--no-pager")
- .output()
+const XKB_RULES_FILE_NAME: &str = "/usr/share/X11/xkb/rules/evdev.lst";
+
+/// Getting list of awailable keyboard layouts from from xkb/rules/evdev.lst file.
+fn get_layouts_blocking() -> std::io::Result<(Vec, Vec, HashMap>)> {
+ use std::fs::File;
+ use std::io::{BufReader, Read};
+
+ let mut file_content = String::new();
{
- Ok(cmd_output) => {
- if cmd_output.status.success() {
- //Etc/UTC
- match String::from_utf8(cmd_output.stdout) {
- Ok(str_tz_list) => Ok(str_tz_list
- .split("\n")
- .filter(|sb| sb.contains('/'))
- .filter_map(|str_t_zone| TimeZoneData::from_str(str_t_zone.trim()))
- .collect::>()),
- Err(ex) => Err(format!("Exception while converting to UTF8 {}", ex)),
- }
- } else {
- Err(format!(
- "Error getting timezones list: {}",
- String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
- ))
- }
- }
- Err(ex) => Err(format!("Exception while trying to list time zones {}", ex)),
+ let locgen_file = File::open(XKB_RULES_FILE_NAME)?;
+ let mut reader = BufReader::new(locgen_file);
+ reader.read_to_string(&mut file_content)?;
}
+
+ let raw_sections: Vec = file_content.split("! ").map(str::to_string).collect();
+
+
+ // Parsing models data
+ let models: Vec = raw_sections
+ .iter()
+ .find(|s| s.starts_with("model"))
+ .and_then(|s| {
+ Some(
+ s.lines()
+ .skip(1)
+ .filter_map(|s| s.trim().split_once(char::is_whitespace))
+ .map(|s_spl| KeyboarModel::from_str_touple(&s_spl))
+ .collect(),
+ )
+ })
+ .unwrap_or_else(Vec::new);
+
+ // Parsing layots data
+ let layouts: Vec = raw_sections
+ .iter()
+ .find(|s| s.starts_with("layout"))
+ .and_then(|s| {
+ Some(
+ s.lines()
+ .skip(1)
+ .filter_map(|s| s.trim().split_once(char::is_whitespace))
+ .map(|s_spl| Layout::from_str_touple(&s_spl))
+ .collect(),
+ )
+ })
+ .unwrap_or_else(Vec::new);
+
+ // parsing variants data to HashMap
+ let mut variants: HashMap> = HashMap::new();
+
+ raw_sections
+ .iter()
+ .find(|s| s.starts_with("variant"))
+ .and_then(|s| {
+ Some(
+ s.lines()
+ .skip(1)
+ .filter_map(|s| s.trim().split_once(char::is_whitespace))
+ .filter_map(|(vrnt, l_plus_descr)| {
+ l_plus_descr
+ .trim()
+ .split_once(": ")
+ .and_then(|(loc, descr)| {
+ Some((loc.trim(), Variant::from_str_touple(&(vrnt, descr))))
+ })
+ })
+ .for_each(|v| {
+ let maybe_key = variants
+ .get_mut(v.0)
+ .and_then(|vrn_vec| Some(vrn_vec.push(v.1)));
+ if maybe_key.is_none() {
+ variants.insert(v.0.to_string(), vec![v.1]);
+ }
+ }),
+ )
+ });
+
+ Ok((models, layouts, variants))
}
-impl TimeZoneStage {
- pub fn new() -> Self {
- match get_timezones_blocking() {
- Ok(time_zones_data_list) => {
- let mut time_zones_map: HashMap> = HashMap::new();
- for tz_data in &time_zones_data_list {
- if let Some(zones) = time_zones_map.get_mut(&tz_data.region) {
- zones.push(tz_data.zone.clone());
- } else {
- time_zones_map.insert(tz_data.region.clone(), vec![tz_data.zone.clone()]);
- }
- }
- // sort zones alphabetically
- time_zones_map.values_mut().for_each(|v| v.sort());
- // sort region names alphabetically
- let mut regions: Vec = time_zones_map.keys().cloned().collect();
- regions.sort();
+impl KeyboardStage {
+ pub fn new() -> Result {
+ match get_layouts_blocking() {
+ Ok((models, layouts, variants)) => {
+
- Self {
- regions: regions,
- zones: Vec::new(),
- time_zones: Ok(time_zones_map),
- region_id: None,
- zone_id: None,
- selected_zone: None,
- selected_zone_text: String::new(),
+ Ok(Self {
+
+ layouts: layouts,
+ variants_map: variants,
+ variants: Vec::new(),
+ model: Some(models[0].clone()),
+ models: models,
+ layout: None,
+ variant: None,
+ keyboard_layouts: Vec::new(),
+ selected_kbd_layout_id: None,
}
+ )
}
Err(ex) => {
- println!(
- "Exception while trying to get time zones list from system {}",
+ let err = format!(
+ "Exception while trying to get kayboard layouts data from system {}",
ex
);
- Self {
- time_zones: Err(ex),
- regions: Vec::new(),
- zones: Vec::new(),
- region_id: None,
- zone_id: None,
- selected_zone: None,
- selected_zone_text: String::new(),
- }
+ Err(err)
}
}
}
diff --git a/src/stages/mod.rs b/src/stages/mod.rs
index f96d128..55242e0 100644
--- a/src/stages/mod.rs
+++ b/src/stages/mod.rs
@@ -20,3 +20,4 @@ pub mod license;
pub mod network;
pub mod timezone;
pub mod locale;
+pub mod keyboard;
\ No newline at end of file