mirror of
https://github.com/mii443/akaza.git
synced 2025-08-22 14:55:31 +00:00
gtk で設定画面をつくった
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -26,10 +26,13 @@ dependencies = [
|
|||||||
name = "akaza-conf"
|
name = "akaza-conf"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"libakaza",
|
"libakaza",
|
||||||
"log",
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
"xdg",
|
"xdg",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1533,9 +1536,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_yaml"
|
name = "serde_yaml"
|
||||||
version = "0.9.16"
|
version = "0.9.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92b5b431e8907b50339b51223b97d102db8d987ced36f6e4d03621db9316c834"
|
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
# 2023-01-24(Tue)
|
||||||
|
|
||||||
|
* 設定画面を gtk4-rs で実装した。
|
||||||
|
|
||||||
# 2022-12-23(Fri)
|
# 2022-12-23(Fri)
|
||||||
|
|
||||||
* Started development, again.
|
* Started development, again.
|
||||||
|
30
README.md
30
README.md
@ -56,36 +56,6 @@ ibus-akaza をインストールしてください。
|
|||||||
|
|
||||||
## 設定方法
|
## 設定方法
|
||||||
|
|
||||||
### config.yml
|
|
||||||
|
|
||||||
XDG の設定ファイルディレクトリ以下、通常であれば `$HOME/.config/akaza/config.yml` に設定ファイルを書くことができます。
|
|
||||||
|
|
||||||
設定可能な項目は以下のもの。
|
|
||||||
|
|
||||||
* ユーザー辞書の設定
|
|
||||||
|
|
||||||
サンプルの設定は以下のような感じになります。
|
|
||||||
akaza が提供しているシステム辞書は偏りがすごくあるので、SKK-JISYO.L を読み込むことをおすすめします。たとえば以下のように設定すると良いでしょう。
|
|
||||||
|
|
||||||
---
|
|
||||||
dicts:
|
|
||||||
- path: /usr/share/skk/SKK-JISYO.L
|
|
||||||
encoding: EucJp
|
|
||||||
dict_type: SKK
|
|
||||||
- path: /usr/share/skk/SKK-JISYO.jinmei
|
|
||||||
encoding: EucJp
|
|
||||||
dict_type: SKK
|
|
||||||
single_term:
|
|
||||||
- path: /usr/share/akaza/SKK-JISYO.dynamic
|
|
||||||
encoding: Utf8
|
|
||||||
dict_type: SKK
|
|
||||||
|
|
||||||
akaza に付属する SKK-JISYO.dynamic を利用すると、「きょう」を変換すると、今日の日付がでるという機能が利用可能です。
|
|
||||||
|
|
||||||
↓ かな入力したい場合は以下のように設定してください。
|
|
||||||
|
|
||||||
romkan: kana
|
|
||||||
|
|
||||||
### Keymap の設定
|
### Keymap の設定
|
||||||
|
|
||||||
Akaza は典型的には以下の順番で探します。
|
Akaza は典型的には以下の順番で探します。
|
||||||
|
@ -10,3 +10,6 @@ xdg = "2.4.1"
|
|||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
libakaza = { path = "../libakaza" }
|
libakaza = { path = "../libakaza" }
|
||||||
|
anyhow = "1.0.68"
|
||||||
|
serde = "1.0.152"
|
||||||
|
serde_yaml = "0.9.17"
|
||||||
|
@ -1,65 +1,109 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use gtk::glib::signal::Inhibit;
|
use gtk::glib::signal::Inhibit;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Application, ApplicationWindow, Button, Label, Notebook};
|
use gtk::{Application, ApplicationWindow, Button, Label, Notebook};
|
||||||
use gtk4 as gtk;
|
use gtk4 as gtk;
|
||||||
use gtk4::builders::ComboBoxTextBuilder;
|
|
||||||
use gtk4::gio::ApplicationFlags;
|
use gtk4::gio::ApplicationFlags;
|
||||||
use gtk4::{ComboBoxText, Grid};
|
use gtk4::{ComboBoxText, Grid};
|
||||||
use libakaza::config::Config;
|
use log::{error, info};
|
||||||
use log::info;
|
|
||||||
|
|
||||||
pub fn open_configuration_window() {
|
use libakaza::config::{Config, DictUsage};
|
||||||
|
|
||||||
|
pub fn open_configuration_window() -> Result<()> {
|
||||||
|
let config = Arc::new(Mutex::new(Config::load()?));
|
||||||
let app = Application::new(Some("com.github.akaza.config"), ApplicationFlags::empty());
|
let app = Application::new(Some("com.github.akaza.config"), ApplicationFlags::empty());
|
||||||
|
|
||||||
app.connect_activate(|app| {
|
app.connect_activate(move |app| {
|
||||||
let window = ApplicationWindow::builder()
|
connect_activate(app, config.clone()).unwrap();
|
||||||
.application(app)
|
|
||||||
.default_width(320)
|
|
||||||
.default_height(200)
|
|
||||||
.title("Akaza の設定")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let notebook = Notebook::builder().build();
|
|
||||||
notebook.append_page(&build_core_pane(), Some(&Label::new(Some("基本設定"))));
|
|
||||||
notebook.append_page(&build_dict_pane(), Some(&Label::new(Some("辞書"))));
|
|
||||||
notebook.append_page(&build_about_pane(), Some(&Label::new(Some("アバウト"))));
|
|
||||||
|
|
||||||
let grid = Grid::builder().build();
|
|
||||||
|
|
||||||
grid.attach(¬ebook, 0, 0, 6, 1);
|
|
||||||
|
|
||||||
let ok_button = Button::with_label("OK");
|
|
||||||
ok_button.connect_clicked(|_| {
|
|
||||||
eprintln!("Save the configuration...");
|
|
||||||
// TODO: 保存処理
|
|
||||||
});
|
|
||||||
let cancel_button = Button::with_label("Cancel");
|
|
||||||
{
|
|
||||||
let window_clone = window.clone();
|
|
||||||
cancel_button.connect_clicked(move |_| {
|
|
||||||
eprintln!("Close the configuration window!");
|
|
||||||
window_clone.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
grid.attach(&ok_button, 4, 1, 1, 1);
|
|
||||||
grid.attach(&cancel_button, 5, 1, 1, 1);
|
|
||||||
|
|
||||||
window.set_child(Some(&grid));
|
|
||||||
|
|
||||||
window.connect_close_request(move |window| {
|
|
||||||
if let Some(application) = window.application() {
|
|
||||||
application.remove_window(window);
|
|
||||||
}
|
|
||||||
Inhibit(false)
|
|
||||||
});
|
|
||||||
|
|
||||||
window.show();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let v: Vec<String> = Vec::new();
|
let v: Vec<String> = Vec::new();
|
||||||
app.run_with_args(v.as_slice());
|
app.run_with_args(v.as_slice());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect_activate(app: &Application, config: Arc<Mutex<Config>>) -> Result<()> {
|
||||||
|
let window = ApplicationWindow::builder()
|
||||||
|
.application(app)
|
||||||
|
.default_width(320)
|
||||||
|
.default_height(200)
|
||||||
|
.title("Akaza の設定")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let notebook = Notebook::builder().build();
|
||||||
|
notebook.append_page(
|
||||||
|
&build_core_pane(config.clone())?,
|
||||||
|
Some(&Label::new(Some("基本設定"))),
|
||||||
|
);
|
||||||
|
notebook.append_page(
|
||||||
|
&build_dict_pane(config.clone()),
|
||||||
|
Some(&Label::new(Some("辞書"))),
|
||||||
|
);
|
||||||
|
notebook.append_page(&build_about_pane(), Some(&Label::new(Some("アバウト"))));
|
||||||
|
|
||||||
|
let grid = Grid::builder().build();
|
||||||
|
|
||||||
|
grid.attach(¬ebook, 0, 0, 6, 1);
|
||||||
|
|
||||||
|
let ok_button = Button::with_label("OK");
|
||||||
|
let config = config;
|
||||||
|
ok_button.connect_clicked(move |_| {
|
||||||
|
eprintln!("Save the configuration...");
|
||||||
|
// TODO: 保存処理
|
||||||
|
let config = config.lock().unwrap();
|
||||||
|
let config = Config {
|
||||||
|
keymap: config.keymap.to_string(),
|
||||||
|
romkan: config.romkan.to_string(),
|
||||||
|
model: config.model.to_string(),
|
||||||
|
dicts: config.dicts.clone(),
|
||||||
|
};
|
||||||
|
info!("Saving config: {}", serde_yaml::to_string(&config).unwrap());
|
||||||
|
|
||||||
|
config.save().unwrap();
|
||||||
|
|
||||||
|
// 最後に ibus restart をしちゃおう。設定の再読み込みとか実装するのは大変。
|
||||||
|
let output = Command::new("ibus").arg("restart").output().unwrap();
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
error!(
|
||||||
|
"Cannot run `ibus restart`: out={}, err={}",
|
||||||
|
String::from_utf8(output.stdout).unwrap(),
|
||||||
|
String::from_utf8(output.stderr).unwrap()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"Ran `ibus restart`: out={}, err={}",
|
||||||
|
String::from_utf8(output.stdout).unwrap(),
|
||||||
|
String::from_utf8(output.stderr).unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let cancel_button = Button::with_label("Cancel");
|
||||||
|
{
|
||||||
|
let window_clone = window.clone();
|
||||||
|
cancel_button.connect_clicked(move |_| {
|
||||||
|
eprintln!("Close the configuration window!");
|
||||||
|
window_clone.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
grid.attach(&ok_button, 4, 1, 1, 1);
|
||||||
|
grid.attach(&cancel_button, 5, 1, 1, 1);
|
||||||
|
|
||||||
|
window.set_child(Some(&grid));
|
||||||
|
|
||||||
|
window.connect_close_request(move |window| {
|
||||||
|
if let Some(application) = window.application() {
|
||||||
|
application.remove_window(window);
|
||||||
|
}
|
||||||
|
Inhibit(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
window.show();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -68,7 +112,7 @@ struct PathConfItem {
|
|||||||
path: String,
|
path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_keymap_list<P>(path: &str, filter: P) -> Vec<PathConfItem>
|
fn get_list<P>(path: &str, filter: P) -> Vec<PathConfItem>
|
||||||
where
|
where
|
||||||
P: FnMut(&&PathBuf) -> bool,
|
P: FnMut(&&PathBuf) -> bool,
|
||||||
{
|
{
|
||||||
@ -85,13 +129,14 @@ where
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_string(),
|
.to_string()
|
||||||
|
.replace(".yml", ""),
|
||||||
path: f.to_string_lossy().to_string(),
|
path: f.to_string_lossy().to_string(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_core_pane() -> Grid {
|
fn build_core_pane(config: Arc<Mutex<Config>>) -> anyhow::Result<Grid> {
|
||||||
// キーマップとローマ字テーブル、モデルの設定ができるようにする。
|
// キーマップとローマ字テーブル、モデルの設定ができるようにする。
|
||||||
let grid = Grid::new();
|
let grid = Grid::new();
|
||||||
// xalign: 0 は左寄という意味。
|
// xalign: 0 は左寄という意味。
|
||||||
@ -104,13 +149,20 @@ fn build_core_pane() -> Grid {
|
|||||||
);
|
);
|
||||||
grid.attach(
|
grid.attach(
|
||||||
&{
|
&{
|
||||||
//TODO: 設定ファイルに書いてあるものを default value として選ぶ
|
|
||||||
let cbt = ComboBoxText::new();
|
let cbt = ComboBoxText::new();
|
||||||
let romkan = get_keymap_list("keymap", { |f| f.to_string_lossy().ends_with(".yml") });
|
let keymap = get_list("keymap", { |f| f.to_string_lossy().ends_with(".yml") });
|
||||||
info!("keymap: {:?}", romkan);
|
for item in keymap {
|
||||||
for item in romkan {
|
|
||||||
cbt.append(Some(&item.path), &item.name);
|
cbt.append(Some(&item.path), &item.name);
|
||||||
}
|
}
|
||||||
|
cbt.set_active_id(Some(&config.lock().unwrap().keymap));
|
||||||
|
{
|
||||||
|
let config = config.clone();
|
||||||
|
cbt.connect_changed(move |f| {
|
||||||
|
if let Some(id) = f.active_id() {
|
||||||
|
config.lock().unwrap().keymap = id.to_string();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
cbt
|
cbt
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
@ -131,11 +183,20 @@ fn build_core_pane() -> Grid {
|
|||||||
grid.attach(
|
grid.attach(
|
||||||
&{
|
&{
|
||||||
let cbt = ComboBoxText::new();
|
let cbt = ComboBoxText::new();
|
||||||
let romkan = get_keymap_list("romkan", { |f| f.to_string_lossy().ends_with(".yml") });
|
let romkan = get_list("romkan", { |f| f.to_string_lossy().ends_with(".yml") });
|
||||||
info!("romkan: {:?}", romkan);
|
info!("romkan: {:?}", romkan);
|
||||||
for item in romkan {
|
for item in romkan {
|
||||||
cbt.append(Some(&item.path), &item.name);
|
cbt.append(Some(&item.path), &item.name);
|
||||||
}
|
}
|
||||||
|
cbt.set_active_id(Some(&config.lock().unwrap().romkan));
|
||||||
|
|
||||||
|
let config = config.clone();
|
||||||
|
cbt.connect_changed(move |f| {
|
||||||
|
if let Some(id) = f.active_id() {
|
||||||
|
config.lock().unwrap().romkan = id.to_string();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
cbt
|
cbt
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
@ -153,13 +214,21 @@ fn build_core_pane() -> Grid {
|
|||||||
grid.attach(
|
grid.attach(
|
||||||
&{
|
&{
|
||||||
let cbt = ComboBoxText::new();
|
let cbt = ComboBoxText::new();
|
||||||
let romkan = get_keymap_list("model", {
|
let model = get_list("model", {
|
||||||
|f| !f.file_name().unwrap().to_string_lossy().starts_with('.')
|
|f| !f.file_name().unwrap().to_string_lossy().starts_with('.')
|
||||||
});
|
});
|
||||||
info!("model: {:?}", romkan);
|
info!("model: {:?}", model);
|
||||||
for item in romkan {
|
for item in model {
|
||||||
cbt.append(Some(&item.path), &item.name);
|
cbt.append(Some(&item.path), &item.name);
|
||||||
}
|
}
|
||||||
|
cbt.set_active_id(Some(&config.lock().unwrap().model));
|
||||||
|
|
||||||
|
cbt.connect_changed(move |f| {
|
||||||
|
if let Some(id) = f.active_id() {
|
||||||
|
config.lock().unwrap().model = id.to_string();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
cbt
|
cbt
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
@ -167,11 +236,59 @@ fn build_core_pane() -> Grid {
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
grid
|
Ok(grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_dict_pane() -> Label {
|
fn build_dict_pane(config: Arc<Mutex<Config>>) -> Grid {
|
||||||
Label::new(Some("(工事中)"))
|
let grid = Grid::builder().column_spacing(10).build();
|
||||||
|
// TODO /usr/share/skk/ 以下のものを拾ってきて入れる
|
||||||
|
for (i, dict_config) in config.lock().unwrap().dicts.iter().enumerate() {
|
||||||
|
grid.attach(
|
||||||
|
&Label::builder()
|
||||||
|
.xalign(0_f32)
|
||||||
|
.label(dict_config.path.as_str())
|
||||||
|
.build(),
|
||||||
|
0,
|
||||||
|
i as i32,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let cbt = ComboBoxText::builder().build();
|
||||||
|
for usage in vec![
|
||||||
|
DictUsage::Normal,
|
||||||
|
DictUsage::SingleTerm,
|
||||||
|
DictUsage::Disabled,
|
||||||
|
] {
|
||||||
|
cbt.append(Some(usage.as_str()), usage.text_jp());
|
||||||
|
}
|
||||||
|
cbt.set_active_id(Some(dict_config.usage.as_str()));
|
||||||
|
{
|
||||||
|
let config = config.clone();
|
||||||
|
let path = dict_config.path.clone();
|
||||||
|
cbt.connect_changed(move |f| {
|
||||||
|
if let Some(id) = f.active_id() {
|
||||||
|
let mut config = config.lock().unwrap();
|
||||||
|
for mut dict in &mut config.dicts {
|
||||||
|
if dict.path == path {
|
||||||
|
dict.usage = DictUsage::from(&id).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
grid.attach(&cbt, 1, i as i32, 1, 1);
|
||||||
|
|
||||||
|
grid.attach(
|
||||||
|
&Label::new(Some(dict_config.dict_type.as_str())),
|
||||||
|
2,
|
||||||
|
i as i32,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
grid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_about_pane() -> Label {
|
fn build_about_pane() -> Label {
|
||||||
|
@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use libakaza::config::{Config, DictConfig, DictEncoding, DictType};
|
use libakaza::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
|
||||||
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder;
|
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder;
|
||||||
use libakaza::user_side_data::user_data::UserData;
|
use libakaza::user_side_data::user_data::UserData;
|
||||||
|
|
||||||
@ -16,14 +16,15 @@ pub fn check(yomi: &str, expected: Option<String>, user_data: bool) -> anyhow::R
|
|||||||
dict_type: DictType::SKK,
|
dict_type: DictType::SKK,
|
||||||
encoding: DictEncoding::EucJp,
|
encoding: DictEncoding::EucJp,
|
||||||
path: "/usr/share/skk/SKK-JISYO.L".to_string(),
|
path: "/usr/share/skk/SKK-JISYO.L".to_string(),
|
||||||
|
usage: DictUsage::Normal,
|
||||||
},
|
},
|
||||||
DictConfig {
|
DictConfig {
|
||||||
dict_type: DictType::SKK,
|
dict_type: DictType::SKK,
|
||||||
encoding: DictEncoding::Utf8,
|
encoding: DictEncoding::Utf8,
|
||||||
path: "data/SKK-JISYO.akaza".to_string(),
|
path: "data/SKK-JISYO.akaza".to_string(),
|
||||||
|
usage: DictUsage::Normal,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
single_term: Default::default(),
|
|
||||||
romkan: Default::default(),
|
romkan: Default::default(),
|
||||||
keymap: Default::default(),
|
keymap: Default::default(),
|
||||||
model: Default::default(),
|
model: Default::default(),
|
||||||
|
@ -5,7 +5,7 @@ use std::time::SystemTime;
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use libakaza::config::{Config, DictConfig, DictEncoding, DictType};
|
use libakaza::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
|
||||||
use libakaza::engine::base::HenkanEngine;
|
use libakaza::engine::base::HenkanEngine;
|
||||||
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder;
|
use libakaza::engine::bigram_word_viterbi_engine::BigramWordViterbiEngineBuilder;
|
||||||
|
|
||||||
@ -58,6 +58,7 @@ pub fn evaluate(
|
|||||||
dict_type: DictType::SKK,
|
dict_type: DictType::SKK,
|
||||||
encoding: DictEncoding::EucJp,
|
encoding: DictEncoding::EucJp,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
|
usage: DictUsage::Normal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +67,12 @@ pub fn evaluate(
|
|||||||
dict_type: DictType::SKK,
|
dict_type: DictType::SKK,
|
||||||
encoding: DictEncoding::Utf8,
|
encoding: DictEncoding::Utf8,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
|
usage: DictUsage::Normal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut builder = BigramWordViterbiEngineBuilder::new(Config {
|
let mut builder = BigramWordViterbiEngineBuilder::new(Config {
|
||||||
dicts,
|
dicts,
|
||||||
single_term: Default::default(),
|
|
||||||
romkan: Default::default(),
|
romkan: Default::default(),
|
||||||
keymap: Default::default(),
|
keymap: Default::default(),
|
||||||
model: Default::default(),
|
model: Default::default(),
|
||||||
|
@ -102,7 +102,10 @@ impl AkazaContext {
|
|||||||
) {
|
) {
|
||||||
debug!("do_property_activate: {}, {}", prop_name, prop_state);
|
debug!("do_property_activate: {}, {}", prop_name, prop_state);
|
||||||
if prop_name == "PrefPane" {
|
if prop_name == "PrefPane" {
|
||||||
open_configuration_window();
|
match open_configuration_window() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => info!("Err: {}", e),
|
||||||
|
}
|
||||||
} else if prop_state == IBusPropState_PROP_STATE_CHECKED
|
} else if prop_state == IBusPropState_PROP_STATE_CHECKED
|
||||||
&& prop_name.starts_with("InputMode.")
|
&& prop_name.starts_with("InputMode.")
|
||||||
{
|
{
|
||||||
|
@ -38,8 +38,8 @@ impl KeyMap {
|
|||||||
unsafe { ibus_keyval_from_name(cs.as_ptr()) }
|
unsafe { ibus_keyval_from_name(cs.as_ptr()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(keymap_name: String) -> anyhow::Result<Self> {
|
pub(crate) fn new(keymap_path: String) -> anyhow::Result<Self> {
|
||||||
let keymap = Keymap::load(keymap_name.as_str())?;
|
let keymap = Keymap::load(keymap_path.as_str())?;
|
||||||
let mut mapping: HashMap<KeyPattern, String> = HashMap::new();
|
let mut mapping: HashMap<KeyPattern, String> = HashMap::new();
|
||||||
|
|
||||||
for (key_pattern, command) in keymap {
|
for (key_pattern, command) in keymap {
|
||||||
|
@ -8,17 +8,21 @@ dicts:
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::{BufReader, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Result};
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use DictEncoding::Utf8;
|
use DictEncoding::Utf8;
|
||||||
|
|
||||||
|
use crate::config::DictUsage::{Normal, SingleTerm};
|
||||||
|
use crate::resource::detect_resource_path;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub dicts: Vec<DictConfig>,
|
pub dicts: Vec<DictConfig>,
|
||||||
pub single_term: Vec<DictConfig>,
|
|
||||||
|
|
||||||
/// ローマ字かな変換テーブルの指定
|
/// ローマ字かな変換テーブルの指定
|
||||||
/// "default", "kana", etc.
|
/// "default", "kana", etc.
|
||||||
@ -37,15 +41,15 @@ pub struct Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_romkan() -> String {
|
fn default_romkan() -> String {
|
||||||
"default".to_string()
|
detect_resource_path("romkan", "default").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_keymap() -> String {
|
fn default_keymap() -> String {
|
||||||
"default".to_string()
|
detect_resource_path("keymap", "default").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_model() -> String {
|
fn default_model() -> String {
|
||||||
"default".to_string()
|
detect_resource_path("model", "default").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -56,9 +60,22 @@ impl Config {
|
|||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load() -> Result<Self> {
|
pub fn file_name() -> Result<PathBuf> {
|
||||||
let basedir = xdg::BaseDirectories::with_prefix("akaza")?;
|
let basedir = xdg::BaseDirectories::with_prefix("akaza")?;
|
||||||
let configfile = basedir.get_config_file("config.yml");
|
Ok(basedir.get_config_file("config.yml"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) -> Result<()> {
|
||||||
|
let file_name = Self::file_name()?;
|
||||||
|
let yml = serde_yaml::to_string(self)?;
|
||||||
|
info!("Write to file: {}", file_name.to_str().unwrap());
|
||||||
|
let mut fp = File::create(file_name)?;
|
||||||
|
fp.write_all(yml.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Result<Self> {
|
||||||
|
let configfile = Self::file_name()?;
|
||||||
let config = match Config::load_from_file(configfile.to_str().unwrap()) {
|
let config = match Config::load_from_file(configfile.to_str().unwrap()) {
|
||||||
Ok(config) => config,
|
Ok(config) => config,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -85,11 +102,11 @@ pub struct DictConfig {
|
|||||||
|
|
||||||
/// Encoding of the dictionary
|
/// Encoding of the dictionary
|
||||||
/// Default: UTF-8
|
/// Default: UTF-8
|
||||||
// #[serde(default = "default_encoding")]
|
|
||||||
pub encoding: DictEncoding,
|
pub encoding: DictEncoding,
|
||||||
|
|
||||||
// #[serde(default = "default_dict_type")]
|
|
||||||
pub dict_type: DictType,
|
pub dict_type: DictType,
|
||||||
|
|
||||||
|
pub usage: DictUsage,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_encoding() -> DictEncoding {
|
fn default_encoding() -> DictEncoding {
|
||||||
@ -121,7 +138,7 @@ impl Display for DictEncoding {
|
|||||||
impl DictEncoding {
|
impl DictEncoding {
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
DictEncoding::Utf8 => "UTF-8",
|
Utf8 => "UTF-8",
|
||||||
DictEncoding::EucJp => "EUC-JP",
|
DictEncoding::EucJp => "EUC-JP",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +169,46 @@ impl DictType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum DictUsage {
|
||||||
|
Normal,
|
||||||
|
SingleTerm,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DictUsage {
|
||||||
|
fn default() -> Self {
|
||||||
|
Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DictUsage {
|
||||||
|
pub fn from(s: &str) -> Result<DictUsage> {
|
||||||
|
match s {
|
||||||
|
"Normal" => Ok(Normal),
|
||||||
|
"SingleTerm" => Ok(SingleTerm),
|
||||||
|
"Disabled" => Ok(DictUsage::Disabled),
|
||||||
|
_ => bail!("Unknown name: {:?}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Normal => "Normal",
|
||||||
|
SingleTerm => "SingleTerm",
|
||||||
|
DictUsage::Disabled => "Disabled",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_jp(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Normal => "通常辞書",
|
||||||
|
SingleTerm => "単項",
|
||||||
|
DictUsage::Disabled => "無効",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -166,6 +223,7 @@ mod tests {
|
|||||||
path: "/usr/share/skk/SKK-JISYO.L".to_string(),
|
path: "/usr/share/skk/SKK-JISYO.L".to_string(),
|
||||||
encoding: DictEncoding::EucJp,
|
encoding: DictEncoding::EucJp,
|
||||||
dict_type: DictType::SKK,
|
dict_type: DictType::SKK,
|
||||||
|
usage: DictUsage::Normal,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -4,7 +4,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::config::{Config, DictConfig, DictEncoding, DictType};
|
use crate::config::{Config, DictConfig, DictEncoding, DictType, DictUsage};
|
||||||
use crate::dict::loader::load_dicts_ex;
|
use crate::dict::loader::load_dicts_ex;
|
||||||
use crate::engine::base::HenkanEngine;
|
use crate::engine::base::HenkanEngine;
|
||||||
use crate::graph::candidate::Candidate;
|
use crate::graph::candidate::Candidate;
|
||||||
@ -124,21 +124,22 @@ impl BigramWordViterbiEngineBuilder {
|
|||||||
|
|
||||||
let system_unigram_lm = match &self.model_dir {
|
let system_unigram_lm = match &self.model_dir {
|
||||||
Some(path) => MarisaSystemUnigramLM::load(&format!("{}/unigram.model", path)),
|
Some(path) => MarisaSystemUnigramLM::load(&format!("{}/unigram.model", path)),
|
||||||
None => MarisaSystemUnigramLM::load(
|
None => {
|
||||||
Self::try_load(&format!("{}/unigram.model", model_name))?.as_str(),
|
MarisaSystemUnigramLM::load(Self::try_load(&model_name, "unigram.model")?.as_str())
|
||||||
),
|
}
|
||||||
}?;
|
}?;
|
||||||
let system_bigram_lm = match &self.model_dir {
|
let system_bigram_lm = match &self.model_dir {
|
||||||
Some(path) => MarisaSystemBigramLM::load(&format!("{}/bigram.model", path.clone())),
|
Some(path) => MarisaSystemBigramLM::load(&format!("{}/bigram.model", path.clone())),
|
||||||
None => MarisaSystemBigramLM::load(
|
None => {
|
||||||
Self::try_load(&format!("{}/bigram.model", model_name))?.as_str(),
|
MarisaSystemBigramLM::load(Self::try_load(&model_name, "bigram.model")?.as_str())
|
||||||
),
|
}
|
||||||
}?;
|
}?;
|
||||||
|
// TODO Merge self.model_dir and config.model
|
||||||
let system_dict = match &self.model_dir {
|
let system_dict = match &self.model_dir {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
format!("{}/SKK-JISYO.akaza", path)
|
format!("{}/SKK-JISYO.akaza", path)
|
||||||
}
|
}
|
||||||
None => Self::try_load(&format!("{}/SKK-JISYO.akaza", model_name))?,
|
None => Self::try_load(&model_name, "SKK-JISYO.akaza")?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_data = if let Some(d) = &self.user_data {
|
let user_data = if let Some(d) = &self.user_data {
|
||||||
@ -148,17 +149,33 @@ impl BigramWordViterbiEngineBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let dict = {
|
let dict = {
|
||||||
let mut dicts = self.config.dicts.to_vec();
|
let mut dicts = self
|
||||||
|
.config
|
||||||
|
.dicts
|
||||||
|
.iter()
|
||||||
|
.filter(|it| it.usage == DictUsage::Normal)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
dicts.push(DictConfig {
|
dicts.push(DictConfig {
|
||||||
path: system_dict,
|
path: system_dict,
|
||||||
dict_type: DictType::SKK,
|
dict_type: DictType::SKK,
|
||||||
encoding: DictEncoding::Utf8,
|
encoding: DictEncoding::Utf8,
|
||||||
|
usage: DictUsage::Normal,
|
||||||
});
|
});
|
||||||
|
|
||||||
load_dicts_ex(&dicts, "kana_kanji_cache.marisa")?
|
load_dicts_ex(&dicts, "kana_kanji_cache.marisa")?
|
||||||
};
|
};
|
||||||
|
|
||||||
let single_term = load_dicts_ex(&self.config.single_term, "single_term_cache.marisa")?;
|
let single_term = load_dicts_ex(
|
||||||
|
&self
|
||||||
|
.config
|
||||||
|
.dicts
|
||||||
|
.iter()
|
||||||
|
.filter(|it| it.usage == DictUsage::SingleTerm)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
"single_term_cache.marisa",
|
||||||
|
)?;
|
||||||
|
|
||||||
// 辞書を元に、トライを作成していく。
|
// 辞書を元に、トライを作成していく。
|
||||||
let mut kana_trie = CedarwoodKanaTrie::default();
|
let mut kana_trie = CedarwoodKanaTrie::default();
|
||||||
@ -203,7 +220,7 @@ impl BigramWordViterbiEngineBuilder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_load(name: &str) -> Result<String> {
|
fn try_load(model_dir: &str, name: &str) -> Result<String> {
|
||||||
detect_resource_path("model", name)
|
Ok(model_dir.to_string() + "/" + name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,11 +92,10 @@ pub enum KeyState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Keymap {
|
impl Keymap {
|
||||||
pub fn load(name: &str) -> Result<HashMap<KeyPattern, String>> {
|
pub fn load(keymap_path: &str) -> Result<HashMap<KeyPattern, String>> {
|
||||||
let pathstr = detect_resource_path("keymap", &format!("{}.yml", name))?;
|
info!("Load {}", keymap_path);
|
||||||
info!("Load {}", pathstr);
|
|
||||||
let got: Keymap = serde_yaml::from_reader(BufReader::new(
|
let got: Keymap = serde_yaml::from_reader(BufReader::new(
|
||||||
File::open(&pathstr).with_context(|| pathstr)?,
|
File::open(keymap_path).with_context(|| keymap_path.to_string())?,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
if let Some(parent) = &got.extends {
|
if let Some(parent) = &got.extends {
|
||||||
|
@ -11,10 +11,12 @@ pub fn detect_resource_path(base: &str, name: &str) -> anyhow::Result<String> {
|
|||||||
.with_context(|| "Opening xdg directory with 'akaza' prefix")?;
|
.with_context(|| "Opening xdg directory with 'akaza' prefix")?;
|
||||||
let pathbuf = basedirs.find_data_file(&target_path);
|
let pathbuf = basedirs.find_data_file(&target_path);
|
||||||
let Some(pathbuf) = pathbuf else {
|
let Some(pathbuf) = pathbuf else {
|
||||||
bail!("Cannot find {:?} in XDG_DATA_HOME or XDG_DATA_DIRS(XDG_DATA_HOME={:?}, XDG_DATA_DIRS={:?})",
|
bail!("Cannot find {:?} in XDG_DATA_HOME or XDG_DATA_DIRS(XDG_DATA_HOME={:?}, XDG_DATA_DIRS={:?}, base={:?}, name={:?})",
|
||||||
target_path,
|
target_path,
|
||||||
basedirs.get_data_home().to_string_lossy().to_string(),
|
basedirs.get_data_home().to_string_lossy().to_string(),
|
||||||
basedirs.get_data_dirs().iter().map(|x| x.to_string_lossy().to_string()).collect::<Vec<_>>(),
|
basedirs.get_data_dirs().iter().map(|x| x.to_string_lossy().to_string()).collect::<Vec<_>>(),
|
||||||
|
base,
|
||||||
|
name
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
pathbuf.to_string_lossy().to_string()
|
pathbuf.to_string_lossy().to_string()
|
||||||
|
@ -14,11 +14,10 @@ pub struct RomKanConfig {
|
|||||||
extends: Option<String>,
|
extends: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_romkan_map(name: &str) -> anyhow::Result<HashMap<String, String>> {
|
fn load_romkan_map(file_path: &str) -> anyhow::Result<HashMap<String, String>> {
|
||||||
let pathstr = resource::detect_resource_path("romkan", &format!("{}.yml", name))?;
|
info!("Load {}", file_path);
|
||||||
info!("Load {}", pathstr);
|
|
||||||
let got: RomKanConfig = serde_yaml::from_reader(BufReader::new(
|
let got: RomKanConfig = serde_yaml::from_reader(BufReader::new(
|
||||||
File::open(&pathstr).with_context(|| pathstr)?,
|
File::open(file_path).with_context(|| file_path.to_string())?,
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
if let Some(parent) = got.extends {
|
if let Some(parent) = got.extends {
|
||||||
|
Reference in New Issue
Block a user