summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPeng Wu <alexepico@gmail.com>2022-01-14 10:55:58 +0800
committerPeng Wu <alexepico@gmail.com>2022-01-14 11:00:05 +0800
commit787875a45559952ff65aa8ea608659aedf41f138 (patch)
treed2590b1063d414130275b25c9810abfa1234fbee /src
parent24f0a6add6e92ba1ebf4e480250c0f0ffc2f427c (diff)
downloadibus-libpinyin-787875a45559952ff65aa8ea608659aedf41f138.tar.gz
ibus-libpinyin-787875a45559952ff65aa8ea608659aedf41f138.tar.xz
ibus-libpinyin-787875a45559952ff65aa8ea608659aedf41f138.zip
Write PYEnglishDatabase.cc
Diffstat (limited to 'src')
-rw-r--r--src/PYEnglishDatabase.cc351
-rw-r--r--src/PYEnglishDatabase.h8
-rw-r--r--src/PYEnglishEditor.cc325
3 files changed, 359 insertions, 325 deletions
diff --git a/src/PYEnglishDatabase.cc b/src/PYEnglishDatabase.cc
new file mode 100644
index 0000000..308305a
--- /dev/null
+++ b/src/PYEnglishDatabase.cc
@@ -0,0 +1,351 @@
+/* vim:set et ts=4 sts=4:
+ *
+ * ibus-libpinyin - Intelligent Pinyin engine based on libpinyin for IBus
+ *
+ * Copyright (c) 2021 Peng Wu <alexepico@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "PYEnglishDatabase.h"
+
+namespace PY{
+
+#define DB_BACKUP_TIMEOUT (60)
+
+EnglishDatabase::EnglishDatabase(){
+ m_sqlite = NULL;
+ m_sql = "";
+ m_user_db = "";
+ m_timeout_id = 0;
+ m_timer = g_timer_new ();
+}
+
+EnglishDatabase::~EnglishDatabase(){
+ g_timer_destroy (m_timer);
+ if (m_timeout_id != 0) {
+ saveUserDB ();
+ g_source_remove (m_timeout_id);
+ }
+
+ if (m_sqlite){
+ sqlite3_close (m_sqlite);
+ m_sqlite = NULL;
+ }
+ m_sql = "";
+ m_user_db = NULL;
+}
+
+gboolean
+EnglishDatabase::isDatabaseExisted(const char *filename) {
+ gboolean result = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
+ if (!result)
+ return FALSE;
+
+ sqlite3 *tmp_db = NULL;
+ if (sqlite3_open_v2 (filename, &tmp_db,
+ SQLITE_OPEN_READONLY, NULL) != SQLITE_OK){
+ return FALSE;
+ }
+
+ /* Check the desc table */
+ sqlite3_stmt *stmt = NULL;
+ const char *tail = NULL;
+ m_sql = "SELECT value FROM desc WHERE name = 'version';";
+ result = sqlite3_prepare_v2 (tmp_db, m_sql.c_str(), -1, &stmt, &tail);
+ if (result != SQLITE_OK)
+ return FALSE;
+
+ result = sqlite3_step (stmt);
+ if (result != SQLITE_ROW)
+ return FALSE;
+
+ result = sqlite3_column_type (stmt, 0);
+ if (result != SQLITE_TEXT)
+ return FALSE;
+
+ const char *version = (const char *) sqlite3_column_text (stmt, 0);
+ if (strcmp("1.2.0", version ) != 0)
+ return FALSE;
+
+ result = sqlite3_finalize (stmt);
+ g_assert (result == SQLITE_OK);
+ sqlite3_close (tmp_db);
+ return TRUE;
+}
+
+gboolean
+EnglishDatabase::createDatabase(const char *filename) {
+ /* unlink the old database. */
+ gboolean retval = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
+ if (retval) {
+ int result = g_unlink (filename);
+ if (result == -1)
+ return FALSE;
+ }
+
+ char *dirname = g_path_get_dirname (filename);
+ g_mkdir_with_parents (dirname, 0700);
+ g_free (dirname);
+
+ sqlite3 *tmp_db = NULL;
+ if (sqlite3_open_v2 (filename, &tmp_db,
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
+ return FALSE;
+ }
+
+ /* Create DESCription table */
+ m_sql = "BEGIN TRANSACTION;\n";
+ m_sql << "CREATE TABLE IF NOT EXISTS desc (name TEXT PRIMARY KEY, value TEXT);\n";
+ m_sql << "INSERT OR IGNORE INTO desc VALUES ('version', '1.2.0');";
+ m_sql << "COMMIT;\n";
+
+ if (!executeSQL (tmp_db)) {
+ sqlite3_close (tmp_db);
+ return FALSE;
+ }
+
+ /* Create Schema */
+ m_sql = "CREATE TABLE IF NOT EXISTS english ("
+ "word TEXT NOT NULL PRIMARY KEY,"
+ "freq FLOAT NOT NULL DEFAULT(0)"
+ ");";
+ if (!executeSQL (tmp_db)) {
+ sqlite3_close (tmp_db);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+gboolean
+EnglishDatabase::openDatabase(const char *system_db, const char *user_db){
+ if (!isDatabaseExisted (system_db))
+ return FALSE;
+ if (!isDatabaseExisted (user_db)) {
+ gboolean result = createDatabase (user_db);
+ if (!result)
+ return FALSE;
+ }
+ /* cache the user db name. */
+ m_user_db = user_db;
+
+ /* do database attach here. :) */
+ if (sqlite3_open_v2 (system_db, &m_sqlite,
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
+ m_sqlite = NULL;
+ return FALSE;
+ }
+
+#if 0
+ m_sql.printf (SQL_ATTACH_DB, user_db);
+ if (!executeSQL (m_sqlite)) {
+ sqlite3_close (m_sqlite);
+ m_sqlite = NULL;
+ return FALSE;
+ }
+ return TRUE;
+#endif
+ return loadUserDB();
+}
+
+/* List the words in freq order. */
+gboolean
+EnglishDatabase::listWords(const char *prefix, std::vector<std::string> & words){
+ sqlite3_stmt *stmt = NULL;
+ const char *tail = NULL;
+ words.clear ();
+
+ /* list words */
+ const char *SQL_DB_LIST =
+ "SELECT word FROM ( "
+ "SELECT * FROM english UNION ALL SELECT * FROM userdb.english) "
+ " WHERE word LIKE \"%s%\" GROUP BY word ORDER BY SUM(freq) DESC;";
+ m_sql.printf (SQL_DB_LIST, prefix);
+ int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail);
+ if (result != SQLITE_OK)
+ return FALSE;
+
+ result = sqlite3_step (stmt);
+ while (result == SQLITE_ROW){
+ /* get the words. */
+ result = sqlite3_column_type (stmt, 0);
+ if (result != SQLITE_TEXT)
+ return FALSE;
+
+ const char *word = (const char *)sqlite3_column_text (stmt, 0);
+ words.push_back (word);
+ result = sqlite3_step (stmt);
+ }
+
+ sqlite3_finalize (stmt);
+ if (result != SQLITE_DONE)
+ return FALSE;
+ return TRUE;
+}
+
+/* Get the freq of user sqlite db. */
+gboolean
+EnglishDatabase::getWordInfo(const char *word, float & freq){
+ sqlite3_stmt *stmt = NULL;
+ const char *tail = NULL;
+ /* get word info. */
+ const char *SQL_DB_SELECT =
+ "SELECT freq FROM userdb.english WHERE word = \"%s\";";
+ m_sql.printf (SQL_DB_SELECT, word);
+ int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail);
+ g_assert (result == SQLITE_OK);
+ result = sqlite3_step (stmt);
+ if (result != SQLITE_ROW)
+ return FALSE;
+ result = sqlite3_column_type (stmt, 0);
+ if (result != SQLITE_FLOAT)
+ return FALSE;
+ freq = sqlite3_column_double (stmt, 0);
+ result = sqlite3_finalize (stmt);
+ g_assert (result == SQLITE_OK);
+ return TRUE;
+}
+
+/* Update the freq with delta value. */
+gboolean
+EnglishDatabase::updateWord(const char *word, float freq){
+ const char *SQL_DB_UPDATE =
+ "UPDATE userdb.english SET freq = \"%f\" WHERE word = \"%s\";";
+ m_sql.printf (SQL_DB_UPDATE, freq, word);
+ gboolean retval = executeSQL (m_sqlite);
+ modified ();
+ return retval;
+}
+
+/* Insert the word into user db with the initial freq. */
+gboolean
+EnglishDatabase::insertWord(const char *word, float freq){
+ const char *SQL_DB_INSERT =
+ "INSERT INTO userdb.english (word, freq) VALUES (\"%s\", \"%f\");";
+ m_sql.printf (SQL_DB_INSERT, word, freq);
+ gboolean retval = executeSQL (m_sqlite);
+ modified ();
+ return retval;
+}
+
+gboolean
+EnglishDatabase::executeSQL(sqlite3 *sqlite){
+ gchar *errmsg = NULL;
+ if (sqlite3_exec (sqlite, m_sql.c_str (), NULL, NULL, &errmsg)
+ != SQLITE_OK) {
+ g_warning ("%s: %s", errmsg, m_sql.c_str());
+ sqlite3_free (errmsg);
+ return FALSE;
+ }
+ m_sql.clear ();
+ return TRUE;
+}
+
+gboolean
+EnglishDatabase::loadUserDB (void){
+ sqlite3 *userdb = NULL;
+ /* Attach user database */
+ do {
+ const char *SQL_ATTACH_DB =
+ "ATTACH DATABASE ':memory:' AS userdb;";
+ m_sql.printf (SQL_ATTACH_DB);
+ if (!executeSQL (m_sqlite))
+ break;
+
+ /* Note: user db is always created by openDatabase. */
+ if (sqlite3_open_v2 ( m_user_db, &userdb,
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE, NULL) != SQLITE_OK)
+ break;
+
+ sqlite3_backup *backup = sqlite3_backup_init (m_sqlite, "userdb", userdb, "main");
+
+ if (backup) {
+ sqlite3_backup_step (backup, -1);
+ sqlite3_backup_finish (backup);
+ }
+
+ sqlite3_close (userdb);
+ return TRUE;
+ } while (0);
+
+ if (userdb)
+ sqlite3_close (userdb);
+ return FALSE;
+}
+
+gboolean
+EnglishDatabase::saveUserDB (void){
+ sqlite3 *userdb = NULL;
+ String tmpfile = String(m_user_db) + "-tmp";
+ do {
+ /* remove tmpfile if it exist */
+ g_unlink(tmpfile);
+
+ if (sqlite3_open_v2 (tmpfile, &userdb,
+ SQLITE_OPEN_READWRITE |
+ SQLITE_OPEN_CREATE, NULL) != SQLITE_OK)
+ break;
+
+ sqlite3_backup *backup = sqlite3_backup_init (userdb, "main", m_sqlite, "userdb");
+
+ if (backup == NULL)
+ break;
+
+ sqlite3_backup_step (backup, -1);
+ sqlite3_backup_finish (backup);
+ sqlite3_close (userdb);
+
+ g_rename(tmpfile, m_user_db);
+ return TRUE;
+ } while (0);
+
+ if (userdb)
+ sqlite3_close (userdb);
+ g_unlink (tmpfile);
+ return FALSE;
+}
+
+void
+EnglishDatabase::modified (void){
+ /* Restart the timer */
+ g_timer_start (m_timer);
+
+ if (m_timeout_id != 0)
+ return;
+
+ m_timeout_id = g_timeout_add_seconds (DB_BACKUP_TIMEOUT,
+ EnglishDatabase::timeoutCallback,
+ static_cast<gpointer> (this));
+}
+
+static gboolean
+EnglishDatabase::timeoutCallback (gpointer data){
+ EnglishDatabase *self = static_cast<EnglishDatabase *> (data);
+
+ /* Get elapsed time since last modification of database. */
+ guint elapsed = (guint) g_timer_elapsed (self->m_timer, NULL);
+
+ if (elapsed >= DB_BACKUP_TIMEOUT &&
+ self->saveUserDB ()) {
+ self->m_timeout_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+};
diff --git a/src/PYEnglishDatabase.h b/src/PYEnglishDatabase.h
index d5b832a..0cfbc3d 100644
--- a/src/PYEnglishDatabase.h
+++ b/src/PYEnglishDatabase.h
@@ -40,6 +40,14 @@ private:
gboolean saveUserDB (void);
void modified (void);
static gboolean timeoutCallback (gpointer data);
+
+private:
+ sqlite3 *m_sqlite;
+ String m_sql;
+ const char *m_user_db;
+
+ guint m_timeout_id;
+ GTimer *m_timer;
};
#endif
diff --git a/src/PYEnglishEditor.cc b/src/PYEnglishEditor.cc
index f5e3123..0b2677f 100644
--- a/src/PYEnglishEditor.cc
+++ b/src/PYEnglishEditor.cc
@@ -34,331 +34,6 @@
namespace PY {
-#define DB_BACKUP_TIMEOUT (60)
-
-class EnglishDatabase{
-public:
- EnglishDatabase(){
- m_sqlite = NULL;
- m_sql = "";
- m_user_db = "";
- m_timeout_id = 0;
- m_timer = g_timer_new ();
- }
-
- ~EnglishDatabase(){
- g_timer_destroy (m_timer);
- if (m_timeout_id != 0) {
- saveUserDB ();
- g_source_remove (m_timeout_id);
- }
-
- if (m_sqlite){
- sqlite3_close (m_sqlite);
- m_sqlite = NULL;
- }
- m_sql = "";
- m_user_db = NULL;
- }
-
- gboolean isDatabaseExisted(const char *filename) {
- gboolean result = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
- if (!result)
- return FALSE;
-
- sqlite3 *tmp_db = NULL;
- if (sqlite3_open_v2 (filename, &tmp_db,
- SQLITE_OPEN_READONLY, NULL) != SQLITE_OK){
- return FALSE;
- }
-
- /* Check the desc table */
- sqlite3_stmt *stmt = NULL;
- const char *tail = NULL;
- m_sql = "SELECT value FROM desc WHERE name = 'version';";
- result = sqlite3_prepare_v2 (tmp_db, m_sql.c_str(), -1, &stmt, &tail);
- if (result != SQLITE_OK)
- return FALSE;
-
- result = sqlite3_step (stmt);
- if (result != SQLITE_ROW)
- return FALSE;
-
- result = sqlite3_column_type (stmt, 0);
- if (result != SQLITE_TEXT)
- return FALSE;
-
- const char *version = (const char *) sqlite3_column_text (stmt, 0);
- if (strcmp("1.2.0", version ) != 0)
- return FALSE;
-
- result = sqlite3_finalize (stmt);
- g_assert (result == SQLITE_OK);
- sqlite3_close (tmp_db);
- return TRUE;
- }
-
- gboolean createDatabase(const char *filename) {
- /* unlink the old database. */
- gboolean retval = g_file_test (filename, G_FILE_TEST_IS_REGULAR);
- if (retval) {
- int result = g_unlink (filename);
- if (result == -1)
- return FALSE;
- }
-
- char *dirname = g_path_get_dirname (filename);
- g_mkdir_with_parents (dirname, 0700);
- g_free (dirname);
-
- sqlite3 *tmp_db = NULL;
- if (sqlite3_open_v2 (filename, &tmp_db,
- SQLITE_OPEN_READWRITE |
- SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
- return FALSE;
- }
-
- /* Create DESCription table */
- m_sql = "BEGIN TRANSACTION;\n";
- m_sql << "CREATE TABLE IF NOT EXISTS desc (name TEXT PRIMARY KEY, value TEXT);\n";
- m_sql << "INSERT OR IGNORE INTO desc VALUES ('version', '1.2.0');";
- m_sql << "COMMIT;\n";
-
- if (!executeSQL (tmp_db)) {
- sqlite3_close (tmp_db);
- return FALSE;
- }
-
- /* Create Schema */
- m_sql = "CREATE TABLE IF NOT EXISTS english ("
- "word TEXT NOT NULL PRIMARY KEY,"
- "freq FLOAT NOT NULL DEFAULT(0)"
- ");";
- if (!executeSQL (tmp_db)) {
- sqlite3_close (tmp_db);
- return FALSE;
- }
- return TRUE;
- }
-
- gboolean openDatabase(const char *system_db, const char *user_db){
- if (!isDatabaseExisted (system_db))
- return FALSE;
- if (!isDatabaseExisted (user_db)) {
- gboolean result = createDatabase (user_db);
- if (!result)
- return FALSE;
- }
- /* cache the user db name. */
- m_user_db = user_db;
-
- /* do database attach here. :) */
- if (sqlite3_open_v2 (system_db, &m_sqlite,
- SQLITE_OPEN_READWRITE |
- SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
- m_sqlite = NULL;
- return FALSE;
- }
-
-#if 0
- m_sql.printf (SQL_ATTACH_DB, user_db);
- if (!executeSQL (m_sqlite)) {
- sqlite3_close (m_sqlite);
- m_sqlite = NULL;
- return FALSE;
- }
- return TRUE;
-#endif
- return loadUserDB();
- }
-
- /* List the words in freq order. */
- gboolean listWords(const char *prefix, std::vector<std::string> & words){
- sqlite3_stmt *stmt = NULL;
- const char *tail = NULL;
- words.clear ();
-
- /* list words */
- const char *SQL_DB_LIST =
- "SELECT word FROM ( "
- "SELECT * FROM english UNION ALL SELECT * FROM userdb.english) "
- " WHERE word LIKE \"%s%\" GROUP BY word ORDER BY SUM(freq) DESC;";
- m_sql.printf (SQL_DB_LIST, prefix);
- int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail);
- if (result != SQLITE_OK)
- return FALSE;
-
- result = sqlite3_step (stmt);
- while (result == SQLITE_ROW){
- /* get the words. */
- result = sqlite3_column_type (stmt, 0);
- if (result != SQLITE_TEXT)
- return FALSE;
-
- const char *word = (const char *)sqlite3_column_text (stmt, 0);
- words.push_back (word);
- result = sqlite3_step (stmt);
- }
-
- sqlite3_finalize (stmt);
- if (result != SQLITE_DONE)
- return FALSE;
- return TRUE;
- }
-
- /* Get the freq of user sqlite db. */
- gboolean getWordInfo(const char *word, float & freq){
- sqlite3_stmt *stmt = NULL;
- const char *tail = NULL;
- /* get word info. */
- const char *SQL_DB_SELECT =
- "SELECT freq FROM userdb.english WHERE word = \"%s\";";
- m_sql.printf (SQL_DB_SELECT, word);
- int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail);
- g_assert (result == SQLITE_OK);
- result = sqlite3_step (stmt);
- if (result != SQLITE_ROW)
- return FALSE;
- result = sqlite3_column_type (stmt, 0);
- if (result != SQLITE_FLOAT)
- return FALSE;
- freq = sqlite3_column_double (stmt, 0);
- result = sqlite3_finalize (stmt);
- g_assert (result == SQLITE_OK);
- return TRUE;
- }
-
- /* Update the freq with delta value. */
- gboolean updateWord(const char *word, float freq){
- const char *SQL_DB_UPDATE =
- "UPDATE userdb.english SET freq = \"%f\" WHERE word = \"%s\";";
- m_sql.printf (SQL_DB_UPDATE, freq, word);
- gboolean retval = executeSQL (m_sqlite);
- modified ();
- return retval;
- }
-
- /* Insert the word into user db with the initial freq. */
- gboolean insertWord(const char *word, float freq){
- const char *SQL_DB_INSERT =
- "INSERT INTO userdb.english (word, freq) VALUES (\"%s\", \"%f\");";
- m_sql.printf (SQL_DB_INSERT, word, freq);
- gboolean retval = executeSQL (m_sqlite);
- modified ();
- return retval;
- }
-
-private:
- gboolean executeSQL(sqlite3 *sqlite){
- gchar *errmsg = NULL;
- if (sqlite3_exec (sqlite, m_sql.c_str (), NULL, NULL, &errmsg)
- != SQLITE_OK) {
- g_warning ("%s: %s", errmsg, m_sql.c_str());
- sqlite3_free (errmsg);
- return FALSE;
- }
- m_sql.clear ();
- return TRUE;
- }
-
- gboolean loadUserDB (void){
- sqlite3 *userdb = NULL;
- /* Attach user database */
- do {
- const char *SQL_ATTACH_DB =
- "ATTACH DATABASE ':memory:' AS userdb;";
- m_sql.printf (SQL_ATTACH_DB);
- if (!executeSQL (m_sqlite))
- break;
-
- /* Note: user db is always created by openDatabase. */
- if (sqlite3_open_v2 ( m_user_db, &userdb,
- SQLITE_OPEN_READWRITE |
- SQLITE_OPEN_CREATE, NULL) != SQLITE_OK)
- break;
-
- sqlite3_backup *backup = sqlite3_backup_init (m_sqlite, "userdb", userdb, "main");
-
- if (backup) {
- sqlite3_backup_step (backup, -1);
- sqlite3_backup_finish (backup);
- }
-
- sqlite3_close (userdb);
- return TRUE;
- } while (0);
-
- if (userdb)
- sqlite3_close (userdb);
- return FALSE;
- }
-
- gboolean saveUserDB (void){
- sqlite3 *userdb = NULL;
- String tmpfile = String(m_user_db) + "-tmp";
- do {
- /* remove tmpfile if it exist */
- g_unlink(tmpfile);
-
- if (sqlite3_open_v2 (tmpfile, &userdb,
- SQLITE_OPEN_READWRITE |
- SQLITE_OPEN_CREATE, NULL) != SQLITE_OK)
- break;
-
- sqlite3_backup *backup = sqlite3_backup_init (userdb, "main", m_sqlite, "userdb");
-
- if (backup == NULL)
- break;
-
- sqlite3_backup_step (backup, -1);
- sqlite3_backup_finish (backup);
- sqlite3_close (userdb);
-
- g_rename(tmpfile, m_user_db);
- return TRUE;
- } while (0);
-
- if (userdb)
- sqlite3_close (userdb);
- g_unlink (tmpfile);
- return FALSE;
- }
-
- void modified (void){
- /* Restart the timer */
- g_timer_start (m_timer);
-
- if (m_timeout_id != 0)
- return;
-
- m_timeout_id = g_timeout_add_seconds (DB_BACKUP_TIMEOUT,
- EnglishDatabase::timeoutCallback,
- static_cast<gpointer> (this));
- }
-
- static gboolean timeoutCallback (gpointer data){
- EnglishDatabase *self = static_cast<EnglishDatabase *> (data);
-
- /* Get elapsed time since last modification of database. */
- guint elapsed = (guint) g_timer_elapsed (self->m_timer, NULL);
-
- if (elapsed >= DB_BACKUP_TIMEOUT &&
- self->saveUserDB ()) {
- self->m_timeout_id = 0;
- return FALSE;
- }
-
- return TRUE;
- }
-
- sqlite3 *m_sqlite;
- String m_sql;
- const char *m_user_db;
-
- guint m_timeout_id;
- GTimer *m_timer;
-};
-
EnglishEditor::EnglishEditor (PinyinProperties & props, Config &config)
: Editor (props, config), m_train_factor (0.1)
{