+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#include "PYDatabase.h"
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <sqlite3.h>
-#include "PYUtil.h"
-#include "PYPinyinArray.h"
-
-namespace PY {
-
-#define DB_CACHE_SIZE "5000"
-#define DB_INDEX_SIZE (3)
-/* define columns */
-#define DB_COLUMN_USER_FREQ (0)
-#define DB_COLUMN_PHRASE (1)
-#define DB_COLUMN_FREQ (2)
-#define DB_COLUMN_S0 (3)
-
-#define DB_PREFETCH_LEN (6)
-#define DB_BACKUP_TIMEOUT (60)
-
-std::unique_ptr<Database> Database::m_instance;
-
-class Conditions : public std::vector<std::string> {
-public:
- Conditions (void) : std::vector<std::string> (1) {}
-
- void double_ (void) {
- gint i = size ();
- do {
- push_back (at (--i));
- } while (i > 0);
- }
-
- void triple (void) {
- gint i = size ();
- do {
- const std::string & value = std::vector<std::string>::at (--i);
- push_back (value);
- push_back (value);
- } while (i > 0);
- }
-
- void appendVPrintf (gint begin, gint end, const gchar *fmt, va_list args) {
- gchar str[64];
- g_vsnprintf (str, sizeof(str), fmt, args);
- for (gint i = begin; i < end; i++) {
- at (i) += str;
- }
- }
-
- void appendPrintf (gint begin, gint end, const gchar *fmt, ...) {
- va_list args;
- va_start (args, fmt);
- appendVPrintf (begin, end, fmt, args);
- va_end (args);
- }
-};
-
-class SQLStmt {
-public:
- SQLStmt (sqlite3 *db)
- : m_db (db), m_stmt (NULL) {
- g_assert (m_db != NULL);
- }
-
- ~SQLStmt () {
- if (m_stmt != NULL) {
- if (sqlite3_finalize (m_stmt) != SQLITE_OK) {
- g_warning ("destroy sqlite stmt failed!");
- }
- }
- }
-
- gboolean prepare (const String &sql) {
- if (sqlite3_prepare (m_db,
- sql.c_str (),
- sql.size (),
- &m_stmt,
- NULL) != SQLITE_OK) {
- g_warning ("parse sql failed!\n %s", sql.c_str ());
- return FALSE;
- }
-
- return TRUE;
- }
-
- gboolean step (void) {
- switch (sqlite3_step (m_stmt)) {
- case SQLITE_ROW:
- return TRUE;
- case SQLITE_DONE:
- return FALSE;
- default:
- g_warning ("sqlites step error!");
- return FALSE;
- }
- }
-
- const gchar *columnText (guint col) {
- return (const gchar *) sqlite3_column_text (m_stmt, col);
- }
-
- gint columnInt (guint col) {
- return sqlite3_column_int (m_stmt, col);
- }
-
-private:
- sqlite3 *m_db;
- sqlite3_stmt *m_stmt;
-};
-
-Query::Query (const PinyinArray & pinyin,
- guint pinyin_begin,
- guint pinyin_len,
- guint option)
- : m_pinyin (pinyin),
- m_pinyin_begin (pinyin_begin),
- m_pinyin_len (pinyin_len),
- m_option (option)
-{
- g_assert (m_pinyin.size () >= pinyin_begin + pinyin_len);
-}
-
-Query::~Query (void)
-{
-}
-
-gint
-Query::fill (PhraseArray &phrases, gint count)
-{
- gint row = 0;
-
- while (m_pinyin_len > 0) {
- if (G_LIKELY (m_stmt.get () == NULL)) {
- m_stmt = Database::instance ().query (m_pinyin, m_pinyin_begin, m_pinyin_len, -1, m_option);
- g_assert (m_stmt.get () != NULL);
- }
-
- while (m_stmt->step ()) {
- Phrase phrase;
-
- g_strlcpy (phrase.phrase,
- m_stmt->columnText (DB_COLUMN_PHRASE),
- sizeof (phrase.phrase));
- phrase.freq = m_stmt->columnInt (DB_COLUMN_FREQ);
- phrase.user_freq = m_stmt->columnInt (DB_COLUMN_USER_FREQ);
- phrase.len = m_pinyin_len;
-
- for (guint i = 0, column = DB_COLUMN_S0; i < m_pinyin_len; i++) {
- phrase.pinyin_id[i].sheng = m_stmt->columnInt (column++);
- phrase.pinyin_id[i].yun = m_stmt->columnInt (column++);
- }
-
- phrases.push_back (phrase);
- row ++;
- if (G_UNLIKELY (row == count)) {
- return row;
- }
- }
-
- m_stmt.reset ();
- m_pinyin_len --;
- }
-
- return row;
-}
-
-Database::Database (void)
- : m_db (NULL)
- , m_timeout_id (0)
- , m_timer (g_timer_new ())
-{
- open ();
-}
-
-Database::~Database (void)
-{
- g_timer_destroy (m_timer);
- if (m_timeout_id != 0) {
- saveUserDB ();
- g_source_remove (m_timeout_id);
- }
- if (m_db) {
- if (sqlite3_close (m_db) != SQLITE_OK) {
- g_warning ("close sqlite database failed!");
- }
- }
-}
-
-inline gboolean
-Database::executeSQL (const gchar *sql, sqlite3 *db)
-{
- if (db == NULL)
- db = m_db;
-
- gchar *errmsg = NULL;
- if (sqlite3_exec (db, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
- g_warning ("%s: %s", errmsg, sql);
- sqlite3_free (errmsg);
- return FALSE;
- }
- return TRUE;
-}
-
-gboolean
-Database::open (void)
-{
- do {
-#if (SQLITE_VERSION_NUMBER >= 3006000)
- sqlite3_initialize ();
-#endif
- static const gchar * maindb [] = {
- PKGDATADIR"/db/local.db",
- PKGDATADIR"/db/open-phrase.db",
- PKGDATADIR"/db/android.db",
- "main.db",
- };
-
- guint i;
- for (i = 0; i < G_N_ELEMENTS (maindb); i++) {
- if (!g_file_test(maindb[i], G_FILE_TEST_IS_REGULAR))
- continue;
- if (sqlite3_open_v2 (maindb[i], &m_db,
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK) {
- g_message ("Use database %s", maindb[i]);
- break;
- }
- }
-
- if (i == G_N_ELEMENTS (maindb)) {
- g_warning ("can not open main database");
- break;
- }
-
- m_sql.clear ();
-
- /* Set synchronous=OFF, write user database will become much faster.
- * It will cause user database corrupted, if the operatering system
- * crashes or computer loses power.
- * */
- m_sql << "PRAGMA synchronous=OFF;\n";
-
- /* Set the cache size for better performance */
- m_sql << "PRAGMA cache_size=" DB_CACHE_SIZE ";\n";
-
- /* Using memory for temp store */
- // m_sql << "PRAGMA temp_store=MEMORY;\n";
-
- /* Set journal mode */
- // m_sql << "PRAGMA journal_mode=PERSIST;\n";
-
- /* Using EXCLUSIVE locking mode on databases
- * for better performance */
- m_sql << "PRAGMA locking_mode=EXCLUSIVE;\n";
- if (!executeSQL (m_sql))
- break;
-
- loadUserDB ();
-#if 0
- /* Attach user database */
- m_buffer = g_get_user_cache_dir ();
- m_buffer << G_DIR_SEPARATOR_S << "ibus"
- << G_DIR_SEPARATOR_S << "pinyin";
- g_mkdir_with_parents (m_buffer, 0750);
- m_buffer << G_DIR_SEPARATOR_S << "user-1.3.db";
- retval = openUserDB (m_buffer);
- if (!retval) {
- g_warning ("Can not open user database %s", m_buffer.c_str ());
- if (!openUserDB (":memory:"))
- goto _failed;
- }
-#endif
-
- /* prefetch some tables */
- // prefetch ();
-
- return TRUE;
- } while (0);
-
- if (m_db) {
- sqlite3_close (m_db);
- m_db = NULL;
- }
- return FALSE;
-}
-
-gboolean
-Database::loadUserDB (void)
-{
- sqlite3 *userdb = NULL;
- do {
- /* Attach user database */
- m_sql.printf ("ATTACH DATABASE \":memory:\" AS userdb;");
- if (!executeSQL (m_sql))
- break;
-
- m_buffer = g_get_user_cache_dir ();
- m_buffer << G_DIR_SEPARATOR_S << "ibus"
- << G_DIR_SEPARATOR_S << "pinyin";
- g_mkdir_with_parents (m_buffer, 0750);
- m_buffer << G_DIR_SEPARATOR_S << "user-1.3.db";
-
- gint flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
- if (sqlite3_open_v2 (m_buffer, &userdb, flags, NULL) != SQLITE_OK &&
- sqlite3_open_v2 (":memory:", &userdb, flags, NULL) != SQLITE_OK)
- break;
-
- m_sql = "BEGIN TRANSACTION;\n";
- /* create desc table*/
- m_sql << "CREATE TABLE IF NOT EXISTS desc (name PRIMARY KEY, value TEXT);\n";
- m_sql << "INSERT OR IGNORE INTO desc VALUES " << "('version', '1.2.0');\n"
- << "INSERT OR IGNORE INTO desc VALUES " << "('uuid', '" << UUID () << "');\n"
- << "INSERT OR IGNORE INTO desc VALUES " << "('hostname', '" << Hostname () << "');\n"
- << "INSERT OR IGNORE INTO desc VALUES " << "('username', '" << Env ("USERNAME") << "');\n"
- << "INSERT OR IGNORE INTO desc VALUES " << "('create-time', datetime());\n"
- << "INSERT OR IGNORE INTO desc VALUES " << "('attach-time', datetime());\n";
-
- /* create phrase tables */
- for (guint i = 0; i < MAX_PHRASE_LEN; i++) {
- m_sql.appendPrintf ("CREATE TABLE IF NOT EXISTS py_phrase_%d (user_freq, phrase TEXT, freq INTEGER ", i);
- for (guint j = 0; j <= i; j++)
- m_sql.appendPrintf (",s%d INTEGER, y%d INTEGER", j, j);
- m_sql << ");\n";
- }
-
- /* create index */
- m_sql << "CREATE UNIQUE INDEX IF NOT EXISTS " << "index_0_0 ON py_phrase_0(s0,y0,phrase);\n";
- m_sql << "CREATE UNIQUE INDEX IF NOT EXISTS " << "index_1_0 ON py_phrase_1(s0,y0,s1,y1,phrase);\n";
- m_sql << "CREATE INDEX IF NOT EXISTS " << "index_1_1 ON py_phrase_1(s0,s1,y1);\n";
- for (guint i = 2; i < MAX_PHRASE_LEN; i++) {
- m_sql << "CREATE UNIQUE INDEX IF NOT EXISTS " << "index_" << i << "_0 ON py_phrase_" << i
- << "(s0,y0";
- for (guint j = 1; j <= i; j++)
- m_sql << ",s" << j << ",y" << j;
- m_sql << ",phrase);\n";
- m_sql << "CREATE INDEX IF NOT EXISTS " << "index_" << i << "_1 ON py_phrase_" << i << "(s0,s1,s2,y2);\n";
- }
- m_sql << "COMMIT;";
-
- if (!executeSQL (m_sql, userdb))
- break;
-
- sqlite3_backup *backup = sqlite3_backup_init (m_db, "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
-Database::saveUserDB (void)
-{
- m_buffer = g_get_user_cache_dir ();
- m_buffer << G_DIR_SEPARATOR_S << "ibus"
- << G_DIR_SEPARATOR_S << "pinyin";
- g_mkdir_with_parents (m_buffer, 0750);
- m_buffer << G_DIR_SEPARATOR_S << "user-1.3.db";
-
- String tmpfile = m_buffer + "-tmp";
- sqlite3 *userdb = NULL;
- do {
-
- /* remove tmpfile if it exist */
- g_unlink (tmpfile);
-
- gint flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
- if (sqlite3_open_v2 (tmpfile, &userdb, flags, NULL) != SQLITE_OK)
- break;
-
- sqlite3_backup *backup = sqlite3_backup_init (userdb, "main", m_db, "userdb");
-
- if (backup == NULL)
- break;
-
- sqlite3_backup_step (backup, -1);
- sqlite3_backup_finish (backup);
- sqlite3_close (userdb);
-
- g_rename (tmpfile, m_buffer);
-
- return TRUE;
- } while (0);
-
- if (userdb != NULL)
- sqlite3_close (userdb);
- g_unlink (tmpfile);
-
- return FALSE;
-}
-
-void
-Database::prefetch (void)
-{
- m_sql.clear ();
- for (guint i = 0; i < DB_PREFETCH_LEN; i++)
- m_sql << "SELECT * FROM py_phrase_" << i << ";\n";
-
- // g_debug ("prefetching ...");
- executeSQL (m_sql);
- // g_debug ("done");
-}
-
-gboolean
-Database::timeoutCallback (gpointer data)
-{
- Database *self = static_cast<Database*> (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;
-}
-
-void
-Database::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,
- Database::timeoutCallback,
- static_cast<gpointer> (this));
-}
-
-inline static gboolean
-pinyin_option_check_sheng (guint option, gint id, gint fid)
-{
- switch ((id << 16) | fid) {
- case (PINYIN_ID_C << 16) | PINYIN_ID_CH:
- return (option & PINYIN_FUZZY_C_CH);
- case (PINYIN_ID_CH << 16) | PINYIN_ID_C:
- return (option & PINYIN_FUZZY_CH_C);
- case (PINYIN_ID_Z << 16) | PINYIN_ID_ZH:
- return (option & PINYIN_FUZZY_Z_ZH);
- case (PINYIN_ID_ZH << 16) | PINYIN_ID_Z:
- return (option & PINYIN_FUZZY_ZH_Z);
- case (PINYIN_ID_S << 16) | PINYIN_ID_SH:
- return (option & PINYIN_FUZZY_S_SH);
- case (PINYIN_ID_SH << 16) | PINYIN_ID_S:
- return (option & PINYIN_FUZZY_SH_S);
- case (PINYIN_ID_L << 16) | PINYIN_ID_N:
- return (option & PINYIN_FUZZY_L_N);
- case (PINYIN_ID_N << 16) | PINYIN_ID_L:
- return (option & PINYIN_FUZZY_N_L);
- case (PINYIN_ID_F << 16) | PINYIN_ID_H:
- return (option & PINYIN_FUZZY_F_H);
- case (PINYIN_ID_H << 16) | PINYIN_ID_F:
- return (option & PINYIN_FUZZY_H_F);
- case (PINYIN_ID_L << 16) | PINYIN_ID_R:
- return (option & PINYIN_FUZZY_L_R);
- case (PINYIN_ID_R << 16) | PINYIN_ID_L:
- return (option & PINYIN_FUZZY_R_L);
- case (PINYIN_ID_K << 16) | PINYIN_ID_G:
- return (option & PINYIN_FUZZY_K_G);
- case (PINYIN_ID_G << 16) | PINYIN_ID_K:
- return (option & PINYIN_FUZZY_G_K);
- default: return FALSE;
- }
-}
-
-inline static gboolean
-pinyin_option_check_yun (guint option, gint id, gint fid)
-{
- switch ((id << 16) | fid) {
- case (PINYIN_ID_AN << 16) | PINYIN_ID_ANG:
- return (option & PINYIN_FUZZY_AN_ANG);
- case (PINYIN_ID_ANG << 16) | PINYIN_ID_AN:
- return (option & PINYIN_FUZZY_ANG_AN);
- case (PINYIN_ID_EN << 16) | PINYIN_ID_ENG:
- return (option & PINYIN_FUZZY_EN_ENG);
- case (PINYIN_ID_ENG << 16) | PINYIN_ID_EN:
- return (option & PINYIN_FUZZY_ENG_EN);
- case (PINYIN_ID_IN << 16) | PINYIN_ID_ING:
- return (option & PINYIN_FUZZY_IN_ING);
- case (PINYIN_ID_ING << 16) | PINYIN_ID_IN:
- return (option & PINYIN_FUZZY_ING_IN);
- case (PINYIN_ID_IAN << 16) | PINYIN_ID_IANG:
- return (option & PINYIN_FUZZY_IAN_IANG);
- case (PINYIN_ID_IANG << 16) | PINYIN_ID_IAN:
- return (option & PINYIN_FUZZY_IANG_IAN);
- case (PINYIN_ID_UAN << 16) | PINYIN_ID_UANG:
- return (option & PINYIN_FUZZY_UAN_UANG);
- case (PINYIN_ID_UANG << 16) | PINYIN_ID_UAN:
- return (option & PINYIN_FUZZY_UANG_UAN);
- default: return FALSE;
- }
-}
-
-SQLStmtPtr
-Database::query (const PinyinArray &pinyin,
- guint pinyin_begin,
- guint pinyin_len,
- gint m,
- guint option)
-{
- g_assert (pinyin_begin < pinyin.size ());
- g_assert (pinyin_len <= pinyin.size () - pinyin_begin);
- g_assert (pinyin_len <= MAX_PHRASE_LEN);
-
- /* prepare sql */
- Conditions conditions;
-
- for (guint i = 0; i < pinyin_len; i++) {
- const Pinyin *p;
- gboolean fs1, fs2;
- p = pinyin[i + pinyin_begin];
-
- fs1 = pinyin_option_check_sheng (option, p->pinyin_id[0].sheng, p->pinyin_id[1].sheng);
- fs2 = pinyin_option_check_sheng (option, p->pinyin_id[0].sheng, p->pinyin_id[2].sheng);
-
- if (G_LIKELY (i > 0))
- conditions.appendPrintf (0, conditions.size (),
- " AND ");
-
- if (G_UNLIKELY (fs1 || fs2)) {
- if (G_LIKELY (i < DB_INDEX_SIZE)) {
- if (fs1 && fs2 == 0) {
- conditions.double_ ();
- conditions.appendPrintf (0, conditions.size () >> 1,
- "s%d=%d", i, p->pinyin_id[0].sheng);
- conditions.appendPrintf (conditions.size () >> 1, conditions.size (),
- "s%d=%d", i, p->pinyin_id[1].sheng);
- }
- else if (fs1 == 0 && fs2) {
- conditions.double_ ();
- conditions.appendPrintf (0, conditions.size () >> 1,
- "s%d=%d", i, p->pinyin_id[0].sheng);
- conditions.appendPrintf (conditions.size () >> 1, conditions.size (),
- "s%d=%d", i, p->pinyin_id[2].sheng);
- }
- else {
- gint len = conditions.size ();
- conditions.triple ();
- conditions.appendPrintf (0, len,
- "s%d=%d", i, p->pinyin_id[0].sheng);
- conditions.appendPrintf (len, len << 1,
- "s%d=%d", i, p->pinyin_id[1].sheng);
- conditions.appendPrintf (len << 1, conditions.size (),
- "s%d=%d", i, p->pinyin_id[2].sheng);
- }
- }
- else {
- if (fs1 && fs2 == 0) {
- conditions.appendPrintf (0, conditions.size (),
- "s%d IN (%d,%d)", i, p->pinyin_id[0].sheng, p->pinyin_id[1].sheng);
- }
- else if (fs1 == 0 && fs2) {
- conditions.appendPrintf (0, conditions.size (),
- "s%d IN (%d,%d)", i, p->pinyin_id[0].sheng, p->pinyin_id[2].sheng);
- }
- else {
- conditions.appendPrintf (0, conditions.size (),
- "s%d IN (%d,%d,%d)", i, p->pinyin_id[0].sheng, p->pinyin_id[1].sheng, p->pinyin_id[2].sheng);
- }
- }
- }
- else {
- conditions.appendPrintf (0, conditions.size (),
- "s%d=%d", i, p->pinyin_id[0].sheng);
- }
-
- if (p->pinyin_id[0].yun != PINYIN_ID_ZERO) {
- if (pinyin_option_check_yun (option, p->pinyin_id[0].yun, p->pinyin_id[1].yun)) {
- if (G_LIKELY (i < DB_INDEX_SIZE)) {
- conditions.double_ ();
- conditions.appendPrintf (0, conditions.size () >> 1,
- " AND y%d=%d", i, p->pinyin_id[0].yun);
- conditions.appendPrintf (conditions.size () >> 1, conditions.size (),
- " and y%d=%d", i, p->pinyin_id[1].yun);
- }
- else {
- conditions.appendPrintf (0, conditions.size (),
- " AND y%d IN (%d,%d)", i, p->pinyin_id[0].yun, p->pinyin_id[1].yun);
- }
- }
- else {
- conditions.appendPrintf (0, conditions.size (),
- " AND y%d=%d", i, p->pinyin_id[0].yun);
- }
- }
- }
-
-
- m_buffer.clear ();
- for (guint i = 0; i < conditions.size (); i++) {
- if (G_UNLIKELY (i == 0))
- m_buffer << " (" << conditions[i] << ")\n";
- else
- m_buffer << " OR (" << conditions[i] << ")\n";
- }
-
- m_sql.clear ();
- gint id = pinyin_len - 1;
- m_sql << "SELECT * FROM ("
- "SELECT 0 AS user_freq, * FROM main.py_phrase_" << id << " WHERE " << m_buffer << " UNION ALL "
- "SELECT * FROM userdb.py_phrase_" << id << " WHERE " << m_buffer << ") "
- "GROUP BY phrase ORDER BY user_freq DESC, freq DESC";
- if (m > 0)
- m_sql << " LIMIT " << m;
-#if 0
- g_debug ("sql =\n%s", m_sql.c_str ());
-#endif
-
- /* query database */
- SQLStmtPtr stmt (new SQLStmt (m_db));
-
- if (!stmt->prepare (m_sql)) {
- stmt.reset ();
- }
-
- return stmt;
-}
-
-inline void
-Database::phraseWhereSql (const Phrase & p, String & sql)
-{
- sql << " WHERE";
- sql << " s0=" << p.pinyin_id[0].sheng
- << " AND y0=" << p.pinyin_id[0].yun;
- for (guint i = 1; i < p.len; i++) {
- sql << " AND s" << i << '=' << p.pinyin_id[i].sheng
- << " AND y" << i << '=' << p.pinyin_id[i].yun;
- }
- sql << " AND phrase=\"" << p.phrase << "\"";
-
-}
-
-inline void
-Database::phraseSql (const Phrase & p, String & sql)
-{
- sql << "INSERT OR IGNORE INTO userdb.py_phrase_" << p.len - 1
- << " VALUES(" << 0 /* user_freq */
- << ",\"" << p.phrase << '"' /* phrase */
- << ',' << p.freq; /* freq */
-
- for (guint i = 0; i < p.len; i++) {
- sql << ',' << p.pinyin_id[i].sheng << ',' << p.pinyin_id[i].yun;
- }
-
- sql << ");\n";
-
- sql << "UPDATE userdb.py_phrase_" << p.len - 1
- << " SET user_freq=user_freq+1";
-
- phraseWhereSql (p, sql);
- sql << ";\n";
-}
-
-void
-Database::commit (const PhraseArray &phrases)
-{
- Phrase phrase = {""};
-
- m_sql = "BEGIN TRANSACTION;\n";
- for (guint i = 0; i < phrases.size (); i++) {
- phrase += phrases[i];
- phraseSql (phrases[i], m_sql);
- }
- if (phrases.size () > 1)
- phraseSql (phrase, m_sql);
- m_sql << "COMMIT;\n";
-
- executeSQL (m_sql);
- modified ();
-}
-
-void
-Database::remove (const Phrase & phrase)
-{
- m_sql = "BEGIN TRANSACTION;\n";
- m_sql << "DELETE FROM userdb.py_phrase_" << phrase.len - 1;
- phraseWhereSql (phrase, m_sql);
- m_sql << ";\n";
- m_sql << "COMMIT;\n";
-
- executeSQL (m_sql);
- modified ();
-}
-
-void
-Database::init (void)
-{
- if (m_instance.get () == NULL) {
- m_instance.reset (new Database ());
- }
-}
-
-void
-Database::finalize (void)
-{
- m_instance.reset (NULL);
-}
-
-};
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __PY_DATABASE_H_
-#define __PY_DATABASE_H_
-
-#include "PYUtil.h"
-#include "PYString.h"
-#include "PYTypes.h"
-#include "PYPhraseArray.h"
-
-typedef struct sqlite3 sqlite3;
-
-namespace PY {
-
-class PinyinArray;
-struct Phrase;
-
-class SQLStmt;
-typedef std::shared_ptr<SQLStmt> SQLStmtPtr;
-
-class Database;
-
-class Query {
-public:
- Query (const PinyinArray & pinyin,
- guint pinyin_begin,
- guint pinyin_len,
- guint option);
- ~Query (void);
- gint fill (PhraseArray &phrases, gint count);
-
-private:
- const PinyinArray & m_pinyin;
- guint m_pinyin_begin;
- guint m_pinyin_len;
- guint m_option;
- SQLStmtPtr m_stmt;
-};
-
-class Database {
-public:
- ~Database ();
-protected:
- Database ();
-
-public:
- SQLStmtPtr query (const PinyinArray & pinyin,
- guint pinyin_begin,
- guint pinyin_len,
- gint m,
- guint option);
- void commit (const PhraseArray & phrases);
- void remove (const Phrase & phrase);
-
- void conditionsDouble (void);
- void conditionsTriple (void);
-
- static void init (void);
- static void finalize (void);
- static Database & instance (void) { return *m_instance; }
-
-private:
- gboolean open (void);
- gboolean loadUserDB (void);
- gboolean saveUserDB (void);
- void prefetch (void);
- void phraseSql (const Phrase & p, String & sql);
- void phraseWhereSql (const Phrase & p, String & sql);
- gboolean executeSQL (const gchar *sql, sqlite3 *db = NULL);
- void modified (void);
- static gboolean timeoutCallback (gpointer data);
-
-private:
- sqlite3 *m_db; /* sqlite3 database */
-
- String m_sql; /* sql stmt */
- String m_buffer; /* temp buffer */
- guint m_timeout_id;
- GTimer *m_timer;
-
-private:
- static std::unique_ptr<Database> m_instance;
-};
-
-
-};
-
-#endif
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#include "PYPhoneticEditor.h"
-#include "PYConfig.h"
-#include "PYPinyinProperties.h"
-#include "PYSimpTradConverter.h"
-
-namespace PY {
-
-/* init static members */
-PhoneticEditor::PhoneticEditor (PinyinProperties & props, Config & config)
- : Editor (props, config),
- m_pinyin (MAX_PHRASE_LEN),
- m_pinyin_len (0),
- m_buffer (64),
- m_lookup_table (m_config.pageSize ()),
- m_phrase_editor (props, config)
-{
-}
-
-gboolean
-PhoneticEditor::processSpace (guint keyval, guint keycode, guint modifiers)
-{
- if (!m_text)
- return FALSE;
- if (cmshm_filter (modifiers) != 0)
- return TRUE;
- if (m_lookup_table.size () != 0) {
- selectCandidate (m_lookup_table.cursorPos ());
- }
- else {
- commit ();
- }
- return TRUE;
-}
-
-gboolean
-PhoneticEditor::processFunctionKey (guint keyval, guint keycode, guint modifiers)
-{
- if (m_text.empty ())
- return FALSE;
-
- /* ignore numlock */
- modifiers = cmshm_filter (modifiers);
-
- if (modifiers != 0 && modifiers != IBUS_CONTROL_MASK)
- return TRUE;
-
- /* process some cursor control keys */
- if (modifiers == 0) {
- switch (keyval) {
- case IBUS_Return:
- case IBUS_KP_Enter:
- commit ();
- return TRUE;
-
- case IBUS_BackSpace:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- removeCharBefore ();
- }
- return TRUE;
-
- case IBUS_Delete:
- case IBUS_KP_Delete:
- removeCharAfter ();
- return TRUE;
-
- case IBUS_Left:
- case IBUS_KP_Left:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- moveCursorLeft ();
- }
- return TRUE;
-
- case IBUS_Right:
- case IBUS_KP_Right:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- moveCursorRight ();
- }
- return TRUE;
-
- case IBUS_Home:
- case IBUS_KP_Home:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- moveCursorToBegin ();
- }
- return TRUE;
-
- case IBUS_End:
- case IBUS_KP_End:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- moveCursorToEnd ();
- }
- return TRUE;
-
- case IBUS_Up:
- case IBUS_KP_Up:
- cursorUp ();
- return TRUE;
-
- case IBUS_Down:
- case IBUS_KP_Down:
- cursorDown ();
- return TRUE;
-
- case IBUS_Page_Up:
- case IBUS_KP_Page_Up:
- pageUp ();
- return TRUE;
-
- case IBUS_Page_Down:
- case IBUS_KP_Page_Down:
- case IBUS_Tab:
- pageDown ();
- return TRUE;
-
- case IBUS_Escape:
- reset ();
- return TRUE;
- default:
- return TRUE;
- }
- }
- else {
- switch (keyval) {
- case IBUS_BackSpace:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- removeWordBefore ();
- }
- return TRUE;
-
- case IBUS_Delete:
- case IBUS_KP_Delete:
- removeWordAfter ();
- return TRUE;
-
- case IBUS_Left:
- case IBUS_KP_Left:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- moveCursorLeftByWord ();
- }
- return TRUE;
-
- case IBUS_Right:
- case IBUS_KP_Right:
- if (m_phrase_editor.unselectCandidates ()) {
- update ();
- }
- else {
- moveCursorToEnd ();
- }
- return TRUE;
-
- default:
- return TRUE;
- };
- }
- return TRUE;
-}
-
-gboolean
-PhoneticEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
-{
- return FALSE;
-}
-
-gboolean
-PhoneticEditor::updateSpecialPhrases (void)
-{
- guint size = m_special_phrases.size ();
- m_special_phrases.clear ();
-
- if (!m_config.specialPhrases ())
- return FALSE;
-
- if (!m_selected_special_phrase.empty ())
- return FALSE;
-
- guint begin = m_phrase_editor.cursorInChar ();
- guint end = m_cursor;
-
- if (begin < end) {
- SpecialPhraseTable::instance ().lookup (
- m_text.substr (begin, m_cursor - begin),
- m_special_phrases);
- }
-
- return size != m_special_phrases.size () || size != 0;
-}
-
-void
-PhoneticEditor::updateLookupTableFast (void)
-{
- Editor::updateLookupTableFast (m_lookup_table, TRUE);
-}
-
-void
-PhoneticEditor::updateLookupTable (void)
-{
- m_lookup_table.clear ();
-
- fillLookupTableByPage ();
- if (m_lookup_table.size ()) {
- Editor::updateLookupTable (m_lookup_table, TRUE);
- }
- else {
- hideLookupTable ();
- }
-}
-
-gboolean
-PhoneticEditor::fillLookupTableByPage (void)
-{
- if (!m_selected_special_phrase.empty ()) {
- return FALSE;
- }
-
- guint filled_nr = m_lookup_table.size ();
- guint page_size = m_lookup_table.pageSize ();
-
- if (m_special_phrases.size () + m_phrase_editor.candidates ().size () < filled_nr + page_size)
- m_phrase_editor.fillCandidates ();
-
- guint need_nr = MIN (page_size, m_special_phrases.size () + m_phrase_editor.candidates ().size () - filled_nr);
- g_assert (need_nr >= 0);
- if (need_nr == 0) {
- return FALSE;
- }
-
- for (guint i = filled_nr; i < filled_nr + need_nr; i++) {
- if (i < m_special_phrases.size ()) {
- Text text (m_special_phrases[i].c_str ());
- text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x0000ef00, 0, -1);
- m_lookup_table.appendCandidate (text);
- }
- else {
- if (G_LIKELY (m_props.modeSimp ())) {
- Text text (m_phrase_editor.candidate (i - m_special_phrases.size ()));
- if (m_phrase_editor.candidateIsUserPhease (i - m_special_phrases.size ()))
- text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1);
- m_lookup_table.appendCandidate (text);
- }
- else {
- m_buffer.truncate (0);
- SimpTradConverter::simpToTrad (m_phrase_editor.candidate (i - m_special_phrases.size ()), m_buffer);
- Text text (m_buffer);
- if (m_phrase_editor.candidateIsUserPhease (i - m_special_phrases.size ()))
- text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1);
- m_lookup_table.appendCandidate (text);
- }
- }
- }
-
-
- return TRUE;
-}
-
-void
-PhoneticEditor::pageUp (void)
-{
- if (G_LIKELY (m_lookup_table.pageUp ())) {
- updateLookupTableFast ();
- updatePreeditText ();
- updateAuxiliaryText ();
- }
-}
-
-void
-PhoneticEditor::pageDown (void)
-{
- if (G_LIKELY(
- (m_lookup_table.pageDown ()) ||
- (fillLookupTableByPage () && m_lookup_table.pageDown ()))) {
- updateLookupTableFast ();
- updatePreeditText ();
- updateAuxiliaryText ();
- }
-}
-
-void
-PhoneticEditor::cursorUp (void)
-{
- if (G_LIKELY (m_lookup_table.cursorUp ())) {
- updateLookupTableFast ();
- updatePreeditText ();
- updateAuxiliaryText ();
- }
-}
-
-void
-PhoneticEditor::cursorDown (void)
-{
- if (G_LIKELY (
- (m_lookup_table.cursorPos () == m_lookup_table.size () - 1) &&
- (fillLookupTableByPage () == FALSE))) {
- return;
- }
-
- if (G_LIKELY (m_lookup_table.cursorDown ())) {
- updateLookupTableFast ();
- updatePreeditText ();
- updateAuxiliaryText ();
- }
-}
-
-void
-PhoneticEditor::candidateClicked (guint index, guint button, guint state)
-{
- selectCandidateInPage (index);
-}
-
-void
-PhoneticEditor::reset (void)
-{
- m_pinyin.clear ();
- m_pinyin_len = 0;
- m_lookup_table.clear ();
- m_phrase_editor.reset ();
- m_special_phrases.clear ();
- m_selected_special_phrase.clear ();
-
- Editor::reset ();
-}
-
-void
-PhoneticEditor::update (void)
-{
- updateLookupTable ();
- updatePreeditText ();
- updateAuxiliaryText ();
-}
-
-void
-PhoneticEditor::commit (const gchar *str)
-{
- StaticText text(str);
- commitText (text);
-}
-
-gboolean
-PhoneticEditor::selectCandidate (guint i)
-{
- if (i < m_special_phrases.size ()) {
- /* select a special phrase */
- m_selected_special_phrase = m_special_phrases[i];
- if (m_cursor == m_text.size ()) {
- m_buffer = m_phrase_editor.selectedString ();
- m_buffer << m_selected_special_phrase;
- m_phrase_editor.commit ();
- reset ();
- commit ((const gchar *)m_buffer);
- }
- else {
- updateSpecialPhrases ();
- update ();
- }
- return TRUE;
- }
-
- i -= m_special_phrases.size ();
- if (m_phrase_editor.selectCandidate (i)) {
- if (m_phrase_editor.pinyinExistsAfterCursor () ||
- *textAfterPinyin () != '\0') {
- updateSpecialPhrases ();
- update ();
- }
- else {
- commit ();
- }
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-PhoneticEditor::selectCandidateInPage (guint i)
-{
- guint page_size = m_lookup_table.pageSize ();
- guint cursor_pos = m_lookup_table.cursorPos ();
-
- if (G_UNLIKELY (i >= page_size))
- return FALSE;
- i += (cursor_pos / page_size) * page_size;
-
- return selectCandidate (i);
-}
-
-gboolean
-PhoneticEditor::resetCandidate (guint i)
-{
- i -= m_special_phrases.size ();
- if (m_phrase_editor.resetCandidate (i)) {
- update ();
- }
- return TRUE;
-}
-
-gboolean
-PhoneticEditor::resetCandidateInPage (guint i)
-{
- guint page_size = m_lookup_table.pageSize ();
- guint cursor_pos = m_lookup_table.cursorPos ();
- i += (cursor_pos / page_size) * page_size;
-
- return resetCandidate (i);
-}
-
-};
-
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __PY_PHONETIC_EDITOR_H_
-#define __PY_PHONETIC_EDITOR_H_
-
-#include "PYLookupTable.h"
-#include "PYEditor.h"
-#include "PYPinyinParser.h"
-#include "PYPhraseEditor.h"
-#include "PYSpecialPhraseTable.h"
-
-namespace PY {
-
-class SpecialPhraseTable;
-
-class PhoneticEditor : public Editor {
-public:
- PhoneticEditor (PinyinProperties & props, Config & config);
-
-public:
- /* virtual functions */
- virtual void pageUp (void);
- virtual void pageDown (void);
- virtual void cursorUp (void);
- virtual void cursorDown (void);
- virtual void update (void);
- virtual void reset (void);
- virtual void candidateClicked (guint index, guint button, guint state);
- virtual gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers);
- virtual gboolean processSpace (guint keyval, guint keycode, guint modifiers);
- virtual gboolean processFunctionKey (guint keyval, guint keycode, guint modifiers);
- virtual void updateLookupTable ();
- virtual void updateLookupTableFast ();
- virtual gboolean fillLookupTableByPage ();
-
-protected:
-
- gboolean updateSpecialPhrases ();
- gboolean selectCandidate (guint i);
- gboolean selectCandidateInPage (guint i);
- gboolean resetCandidate (guint i);
- gboolean resetCandidateInPage (guint i);
-
- void commit (const gchar *str);
-
- /* inline functions */
- void updatePhraseEditor ()
- {
- m_phrase_editor.update (m_pinyin);
- }
-
- const gchar * textAfterPinyin () const
- {
- return (const gchar *)m_text + m_pinyin_len;
- }
-
- const gchar * textAfterPinyin (guint i) const
- {
- g_assert (i <= m_pinyin.size ());
- if ( G_UNLIKELY (i == 0))
- return m_text;
- i--;
- return (const gchar *)m_text + m_pinyin[i].begin + m_pinyin[i].len;
- }
-
- const gchar * textAfterCursor () const
- {
- return (const gchar *)m_text + m_cursor;
- }
-
- /* pure virtual functions */
- virtual gboolean insert (gint ch) = 0;
- virtual gboolean removeCharBefore (void) = 0;
- virtual gboolean removeCharAfter (void) = 0;
- virtual gboolean removeWordBefore (void) = 0;
- virtual gboolean removeWordAfter (void) = 0;
- virtual gboolean moveCursorLeft (void) = 0;
- virtual gboolean moveCursorRight (void) = 0;
- virtual gboolean moveCursorLeftByWord (void) = 0;
- virtual gboolean moveCursorRightByWord (void) = 0;
- virtual gboolean moveCursorToBegin (void) = 0;
- virtual gboolean moveCursorToEnd (void) = 0;
- virtual void commit (void) = 0;
- virtual void updateAuxiliaryText (void) = 0;
- virtual void updatePreeditText (void) = 0;
-
- /* varibles */
- PinyinArray m_pinyin;
- guint m_pinyin_len;
- String m_buffer;
- LookupTable m_lookup_table;
- PhraseEditor m_phrase_editor;
- std::vector<std::string> m_special_phrases;
- std::string m_selected_special_phrase;
-};
-};
-
-#endif
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#include "PYPhraseEditor.h"
-#include "PYConfig.h"
-#include "PYDatabase.h"
-#include "PYPinyinProperties.h"
-#include "PYSimpTradConverter.h"
-
-namespace PY {
-
-PhraseEditor::PhraseEditor (PinyinProperties & props, Config & config)
- : m_candidates (32),
- m_selected_phrases (8),
- m_selected_string (32),
- m_candidate_0_phrases (8),
- m_pinyin (16),
- m_cursor (0),
- m_props (props),
- m_config (config)
-{
-}
-
-PhraseEditor::~PhraseEditor (void)
-{
-}
-
-gboolean
-PhraseEditor::update (const PinyinArray &pinyin)
-{
- /* the size of pinyin must not bigger than MAX_PHRASE_LEN */
- g_assert (pinyin.size () <= MAX_PHRASE_LEN);
-
- m_pinyin = pinyin;
- m_cursor = 0;
-
- /* FIXME, should not remove all phrases1 */
- m_selected_phrases.clear ();
- m_selected_string.truncate (0);
- updateCandidates ();
- return TRUE;
-}
-
-gboolean
-PhraseEditor::resetCandidate (guint i)
-{
- Database::instance ().remove (m_candidates[i]);
-
- updateCandidates ();
- return TRUE;
-}
-
-void
-PhraseEditor::commit (void)
-{
- Database::instance ().commit (m_selected_phrases);
- reset ();
-}
-
-gboolean
-PhraseEditor::selectCandidate (guint i)
-{
- if (G_UNLIKELY (i >= m_candidates.size ()))
- return FALSE;
-
- if (G_LIKELY (i == 0)) {
- m_selected_phrases.insert (m_selected_phrases.end (),
- m_candidate_0_phrases.begin (),
- m_candidate_0_phrases.end ());
- if (G_LIKELY (m_props.modeSimp ()))
- m_selected_string << m_candidates[0].phrase;
- else
- SimpTradConverter::simpToTrad (m_candidates[0].phrase, m_selected_string);
- m_cursor = m_pinyin.size ();
- }
- else {
- m_selected_phrases.push_back (m_candidates[i]);
- if (G_LIKELY (m_props.modeSimp ()))
- m_selected_string << m_candidates[i].phrase;
- else
- SimpTradConverter::simpToTrad (m_candidates[i].phrase, m_selected_string);
- m_cursor += m_candidates[i].len;
- }
-
- updateCandidates ();
- return TRUE;
-}
-
-void
-PhraseEditor::updateCandidates (void)
-{
- m_candidates.clear ();
- m_query.reset ();
- updateTheFirstCandidate ();
-
- if (G_UNLIKELY (m_pinyin.size () == 0))
- return;
-
- if (G_LIKELY (m_candidate_0_phrases.size () > 1)) {
- Phrase phrase;
- phrase.reset ();
- for (guint i = 0; i < m_candidate_0_phrases.size (); i++)
- phrase += m_candidate_0_phrases[i];
- m_candidates.push_back (phrase);
- }
-
- m_query.reset (new Query (m_pinyin,
- m_cursor,
- m_pinyin.size () - m_cursor,
- m_config.option ()));
- fillCandidates ();
-}
-
-void
-PhraseEditor::updateTheFirstCandidate (void)
-{
- guint begin;
- guint end;
-
- m_candidate_0_phrases.clear ();
-
- if (G_UNLIKELY (m_pinyin.size () == 0))
- return;
-
- begin = m_cursor;
- end = m_pinyin.size ();
-
- while (begin != end) {
- gint ret;
- Query query (m_pinyin,
- begin,
- end - begin,
- m_config.option ());
- ret = query.fill (m_candidate_0_phrases, 1);
- g_assert (ret == 1);
- begin += m_candidate_0_phrases.back ().len;
- }
-}
-
-gboolean
-PhraseEditor::fillCandidates (void)
-{
- if (G_UNLIKELY (m_query.get () == NULL)) {
- return FALSE;
- }
-
- gint ret = m_query->fill (m_candidates, FILL_GRAN);
-
- if (G_UNLIKELY (ret < FILL_GRAN)) {
- /* got all candidates from query */
- m_query.reset ();
- }
-
- return ret > 0 ? TRUE : FALSE;
-}
-
-};
-
-
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __PY_PHRASE_EDITOR_H_
-#define __PY_PHRASE_EDITOR_H_
-
-#include "PYUtil.h"
-#include "PYString.h"
-#include "PYPhraseArray.h"
-#include "PYPinyinArray.h"
-
-#define FILL_GRAN (12)
-
-namespace PY {
-
-class Query;
-class Database;
-class PinyinProperties;
-class Config;
-
-class PhraseEditor {
-public:
- PhraseEditor (PinyinProperties & props, Config & config);
- ~PhraseEditor (void);
-
- const String & selectedString (void) const { return m_selected_string; }
- const PinyinArray & pinyin (void) const { return m_pinyin; }
- const PhraseArray & candidates (void) const { return m_candidates; }
- guint cursor (void) const { return m_cursor; }
-
- guint cursorInChar (void) const
- {
- return m_cursor == 0 ? 0 : m_pinyin[m_cursor - 1].begin + m_pinyin[m_cursor - 1].len;
- }
-
- gboolean pinyinExistsAfterCursor (void) const
- {
- return m_pinyin.size () > m_cursor;
- }
-
- const Phrase & candidate (guint i) const
- {
- return m_candidates[i];
- }
-
- gboolean fillCandidates (void);
-
- const PhraseArray & candidate0 (void) const
- {
- return m_candidate_0_phrases;
- }
-
- gboolean candidateIsUserPhease (guint i) const
- {
- const Phrase & phrase = m_candidates[i];
- return phrase.len > 1 && phrase.user_freq > 0 && phrase.freq == 0;
- }
-
- gboolean unselectCandidates (void)
- {
- if (m_cursor == 0) {
- return FALSE;
- }
- else {
- m_selected_phrases.clear ();
- m_selected_string.truncate (0);
- m_cursor = 0;
- updateCandidates ();
- return TRUE;
- }
- }
-
- void reset (void)
- {
- m_candidates.clear ();
- m_selected_phrases.clear ();
- m_selected_string.truncate (0);
- m_candidate_0_phrases.clear ();
- m_pinyin.clear ();
- m_cursor = 0;
- m_query.reset ();
- }
-
- gboolean update (const PinyinArray &pinyin);
- gboolean selectCandidate (guint i);
- gboolean resetCandidate (guint i);
- void commit (void);
-
- gboolean empty (void) const
- {
- return m_selected_string.empty () && m_candidate_0_phrases.empty ();
- }
-
- operator gboolean (void) const
- {
- return !empty ();
- }
-
-private:
- void updateCandidates (void);
- void updateTheFirstCandidate (void);
-
-private:
- PhraseArray m_candidates; // candidates phrase array
- PhraseArray m_selected_phrases; // selected phrases, before cursor
- String m_selected_string; // selected phrases, in string format
- PhraseArray m_candidate_0_phrases; // the first candidate in phrase array format
- PinyinArray m_pinyin;
- guint m_cursor;
- PinyinProperties & m_props;
- std::shared_ptr<Query> m_query;
- Config & m_config;
-};
-
-};
-
-#endif
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#include "PYPinyinEditor.h"
-#include "PYConfig.h"
-#include "PYPinyinProperties.h"
-#include "PYSimpTradConverter.h"
-#include "PYHalfFullConverter.h"
-
-namespace PY {
-
-/* init static members */
-PinyinEditor::PinyinEditor (PinyinProperties & props, Config & config)
- : PhoneticEditor (props, config)
-{
-}
-
-
-/**
- * process pinyin
- */
-inline gboolean
-PinyinEditor::processPinyin (guint keyval, guint keycode, guint modifiers)
-{
- if (G_UNLIKELY (cmshm_filter (modifiers) != 0))
- return m_text ? TRUE : FALSE;
-
- return insert (keyval);
-}
-
-/**
- * process numbers
- */
-inline gboolean
-PinyinEditor::processNumber (guint keyval, guint keycode, guint modifiers)
-{
- guint i;
-
- if (!m_text)
- return FALSE;
-
- switch (keyval) {
- case IBUS_0:
- case IBUS_KP_0:
- i = 9;
- break;
- case IBUS_1 ... IBUS_9:
- i = keyval - IBUS_1;
- break;
- case IBUS_KP_1 ... IBUS_KP_9:
- i = keyval - IBUS_KP_1;
- break;
- default:
- g_return_val_if_reached (FALSE);
- }
- if (modifiers == 0)
- selectCandidateInPage (i);
- else if ((modifiers & ~IBUS_LOCK_MASK) == IBUS_CONTROL_MASK)
- resetCandidateInPage (i);
- return TRUE;
-}
-
-inline gboolean
-PinyinEditor::processPunct (guint keyval, guint keycode, guint modifiers)
-{
- if (m_text.empty ())
- return FALSE;
-
- if (cmshm_filter (modifiers) != 0)
- return TRUE;
-
- switch (keyval) {
- case IBUS_apostrophe:
- return insert (keyval);
- case IBUS_comma:
- if (m_config.commaPeriodPage ()) {
- pageUp ();
- return TRUE;
- }
- break;
- case IBUS_minus:
- if (m_config.minusEqualPage ()) {
- pageUp ();
- return TRUE;
- }
- break;
- case IBUS_period:
- if (m_config.commaPeriodPage ()) {
- pageDown ();
- return TRUE;
- }
- break;
- case IBUS_equal:
- if (m_config.minusEqualPage ()) {
- pageDown ();
- return TRUE;
- }
- break;
- }
-
- if (m_config.autoCommit ()) {
- if (m_phrase_editor.pinyinExistsAfterCursor ()) {
- selectCandidate (m_lookup_table.cursorPos ());
- }
- commit ();
- return FALSE;
- }
- return TRUE;
-}
-
-inline gboolean
-PinyinEditor::processFunctionKey (guint keyval, guint keycode, guint modifiers)
-{
- if (m_text.empty ())
- return FALSE;
-
- /* ignore numlock */
- modifiers = cmshm_filter (modifiers);
-
- if (modifiers != 0 && modifiers != IBUS_CONTROL_MASK)
- return TRUE;
-
- /* process some cursor control keys */
- if (modifiers == 0) {
- switch (keyval) {
- case IBUS_Shift_L:
- if (!m_config.shiftSelectCandidate ())
- return FALSE;
- selectCandidateInPage (1);
- return TRUE;
-
- case IBUS_Shift_R:
- if (!m_config.shiftSelectCandidate ())
- return FALSE;
- selectCandidateInPage (2);
- return TRUE;
- };
- }
-
- return PhoneticEditor::processFunctionKey (keyval, keycode, modifiers);
-}
-
-gboolean
-PinyinEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
-{
- modifiers &= (IBUS_SHIFT_MASK |
- IBUS_CONTROL_MASK |
- IBUS_MOD1_MASK |
- IBUS_SUPER_MASK |
- IBUS_HYPER_MASK |
- IBUS_META_MASK |
- IBUS_LOCK_MASK);
-
- switch (keyval) {
- /* letters */
- case IBUS_a ... IBUS_z:
- return processPinyin (keyval, keycode, modifiers);
- case IBUS_0 ... IBUS_9:
- case IBUS_KP_0 ... IBUS_KP_9:
- return processNumber (keyval, keycode, modifiers);
- case IBUS_exclam ... IBUS_slash:
- case IBUS_colon ... IBUS_at:
- case IBUS_bracketleft ... IBUS_quoteleft:
- case IBUS_braceleft ... IBUS_asciitilde:
- return processPunct (keyval, keycode, modifiers);
- case IBUS_space:
- return processSpace (keyval, keycode, modifiers);
- default:
- return processFunctionKey (keyval, keycode, modifiers);
- }
-}
-
-void
-PinyinEditor::commit ()
-{
- if (G_UNLIKELY (m_buffer.empty ()))
- return;
-
- m_buffer.clear ();
-
- m_buffer << m_phrase_editor.selectedString ();
-
- const gchar *p;
-
- if (m_selected_special_phrase.empty ()) {
- p = textAfterPinyin (m_buffer.utf8Length ());
- }
- else {
- m_buffer << m_selected_special_phrase;
- p = textAfterCursor ();
- }
-
- if (G_UNLIKELY (m_props.modeFull ())) {
- while (*p != '\0') {
- m_buffer.appendUnichar (HalfFullConverter::toFull (*p++));
- }
- }
- else {
- m_buffer << p;
- }
- m_phrase_editor.commit ();
- reset ();
- PhoneticEditor::commit ((const gchar *)m_buffer);
-}
-
-void
-PinyinEditor::updatePreeditText ()
-{
- /* preedit text = selected phrases + highlight candidate + rest text */
- if (G_UNLIKELY (m_phrase_editor.empty () && m_text.empty ())) {
- hidePreeditText ();
- return;
- }
-
- guint edit_begin = 0;
- guint edit_end = 0;
-
- m_buffer.clear ();
-
- /* add selected phrases */
- m_buffer << m_phrase_editor.selectedString ();
-
- if (G_UNLIKELY (! m_selected_special_phrase.empty ())) {
- /* add selected special phrase */
- m_buffer << m_selected_special_phrase;
- edit_begin = m_buffer.utf8Length ();
-
- /* append text after cursor */
- m_buffer << textAfterCursor ();
- }
- else {
- edit_begin = m_buffer.utf8Length ();
- if (m_lookup_table.size () > 0) {
- guint cursor = m_lookup_table.cursorPos ();
-
- if (cursor < m_special_phrases.size ()) {
- m_buffer << m_special_phrases[cursor].c_str ();
- edit_end = m_buffer.utf8Length ();
- /* append text after cursor */
- m_buffer << textAfterCursor ();
- }
- else {
- const Phrase & candidate = m_phrase_editor.candidate (cursor - m_special_phrases.size ());
- if (m_text.size () == m_cursor) {
- /* cursor at end */
- if (m_props.modeSimp ())
- m_buffer << candidate;
- else
- SimpTradConverter::simpToTrad (candidate, m_buffer);
- edit_end = m_buffer.utf8Length ();
-
- /* append rest text */
- m_buffer << textAfterPinyin (edit_end);
- }
- else {
- guint candidate_end = edit_begin + candidate.len;
- m_buffer << m_pinyin[edit_begin]->sheng << m_pinyin[edit_begin]->yun;
-
- for (guint i = edit_begin + 1; i < candidate_end; i++) {
- m_buffer << ' ' << m_pinyin[i]->sheng << m_pinyin[i]->yun;
- }
- m_buffer << '|' << textAfterPinyin (candidate_end);
- edit_end = m_buffer.utf8Length ();
- }
- }
- }
- else {
- m_buffer << textAfterPinyin ();
- }
- }
-
- StaticText preedit_text (m_buffer);
- /* underline */
- preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
-
- /* candidate */
- if (edit_begin < edit_end) {
- preedit_text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x00000000,
- edit_begin, edit_end);
- preedit_text.appendAttribute (IBUS_ATTR_TYPE_BACKGROUND, 0x00c8c8f0,
- edit_begin, edit_end);
- }
- Editor::updatePreeditText (preedit_text, edit_begin, TRUE);
-}
-
-void
-PinyinEditor::updateAuxiliaryText ()
-{
- /* clear pinyin array */
- if (G_UNLIKELY (m_text.empty () ||
- m_lookup_table.size () == 0)) {
- hideAuxiliaryText ();
- return;
- }
-
- m_buffer.clear ();
-
- updateAuxiliaryTextBefore (m_buffer);
-
- if (m_selected_special_phrase.empty ()) {
- if (m_lookup_table.cursorPos () < m_special_phrases.size ()) {
- guint begin = m_phrase_editor.cursorInChar ();
- m_buffer << m_text.substr (begin, m_cursor - begin)
- << '|' << textAfterCursor ();
- }
- else {
- for (guint i = m_phrase_editor.cursor (); i < m_pinyin.size (); ++i) {
- if (G_LIKELY (i != m_phrase_editor.cursor ()))
- m_buffer << ' ';
- const Pinyin *p = m_pinyin[i];
- m_buffer << p->sheng
- << p->yun;
- }
-
- if (G_UNLIKELY (m_pinyin_len == m_cursor)) {
- /* aux = pinyin + non-pinyin */
- // cursor_pos = m_buffer.utf8Length ();
- m_buffer << '|' << textAfterPinyin ();
- }
- else {
- /* aux = pinyin + ' ' + non-pinyin before cursor + non-pinyin after cursor */
- m_buffer << ' ';
- m_buffer.append (textAfterPinyin (),
- m_cursor - m_pinyin_len);
- // cursor_pos = m_buffer.utf8Length ();
- m_buffer << '|' << textAfterCursor ();
- }
- }
- }
- else {
- if (m_cursor < m_text.size ()) {
- m_buffer << '|' << textAfterCursor ();
- }
- }
-
- updateAuxiliaryTextAfter (m_buffer);
-
- StaticText aux_text (m_buffer);
- Editor::updateAuxiliaryText (aux_text, TRUE);
-}
-
-
-void
-PinyinEditor::updateLookupTable ()
-{
- m_lookup_table.setPageSize (m_config.pageSize ());
- m_lookup_table.setOrientation (m_config.orientation ());
- PhoneticEditor::updateLookupTable ();
-}
-
-};
-
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __PY_PINYIN_EDITOR_H_
-#define __PY_PINYIN_EDITOR_H_
-
-#include "PYPhoneticEditor.h"
-
-namespace PY {
-
-class Config;
-
-#define MAX_PINYIN_LEN 64
-
-class SpecialPhraseTable;
-
-class PinyinEditor : public PhoneticEditor {
-public:
- PinyinEditor (PinyinProperties & props, Config & config);
-
-protected:
- gboolean processPinyin (guint keyval, guint keycode, guint modifiers);
- gboolean processNumber (guint keyval, guint keycode, guint modifiers);
- gboolean processPunct (guint keyval, guint keycode, guint modifiers);
- gboolean processFunctionKey (guint keyval, guint keycode, guint modifiers);
-
- void commit ();
-
- void updateAuxiliaryText (void);
- void updateLookupTable (void);
- void updatePreeditText (void);
-
- virtual gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers);
- virtual void updateAuxiliaryTextBefore (String &buffer) { };
- virtual void updateAuxiliaryTextAfter (String &buffer) { };
-};
-
-};
-
-#endif
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#include "PYPinyinEngine.h"
-#include <string>
-#include "PYConfig.h"
-#include "PYPunctEditor.h"
-#include "PYRawEditor.h"
-#ifdef IBUS_BUILD_LUA_EXTENSION
-#include "PYExtEditor.h"
-#endif
-#ifdef IBUS_BUILD_ENGLISH_INPUT_MODE
-#include "PYEnglishEditor.h"
-#endif
-#include "PYFullPinyinEditor.h"
-#include "PYDoublePinyinEditor.h"
-#include "PYFallbackEditor.h"
-
-namespace PY {
-
-/* constructor */
-PinyinEngine::PinyinEngine (IBusEngine *engine)
- : Engine (engine),
- m_props (PinyinConfig::instance ()),
- m_prev_pressed_key (IBUS_VoidSymbol),
- m_input_mode (MODE_INIT),
- m_fallback_editor (new FallbackEditor (m_props, PinyinConfig::instance ()))
-{
- gint i;
-
- if (PinyinConfig::instance ().doublePinyin ())
- m_editors[MODE_INIT].reset (new DoublePinyinEditor (m_props, PinyinConfig::instance ()));
- else
- m_editors[MODE_INIT].reset (new FullPinyinEditor (m_props, PinyinConfig::instance ()));
-
- m_editors[MODE_PUNCT].reset (new PunctEditor (m_props, PinyinConfig::instance ()));
- m_editors[MODE_RAW].reset (new RawEditor (m_props, PinyinConfig::instance ()));
-#ifdef IBUS_BUILD_LUA_EXTENSION
- m_editors[MODE_EXTENSION].reset (new ExtEditor (m_props, PinyinConfig::instance ()));
-#else
- m_editors[MODE_EXTENSION].reset (new Editor (m_props, PinyinConfig::instance ()));
-#endif
-#ifdef IBUS_BUILD_ENGLISH_INPUT_MODE
- m_editors[MODE_ENGLISH].reset (new EnglishEditor (m_props, PinyinConfig::instance ()));
-#else
- m_editors[MODE_ENGLISH].reset (new Editor (m_props, PinyinConfig::instance ()));
-#endif
-
- m_props.signalUpdateProperty ().connect (std::bind (&PinyinEngine::updateProperty, this, _1));
-
- for (i = MODE_INIT; i < MODE_LAST; i++) {
- connectEditorSignals (m_editors[i]);
- }
-
- connectEditorSignals (m_fallback_editor);
-}
-
-/* destructor */
-PinyinEngine::~PinyinEngine (void)
-{
-}
-
-gboolean
-PinyinEngine::processKeyEvent (guint keyval, guint keycode, guint modifiers)
-{
- gboolean retval = FALSE;
-
- /* check Shift + Release hotkey,
- * and then ignore other Release key event */
- if (modifiers & IBUS_RELEASE_MASK) {
- /* press and release keyval are same,
- * and no other key event between the press and release ket event*/
- if (m_prev_pressed_key == keyval) {
- if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R) {
- if (!m_editors[MODE_INIT]->text ().empty ())
- m_editors[MODE_INIT]->reset ();
- m_props.toggleModeChinese ();
- return TRUE;
- }
- }
-
- if (m_input_mode == MODE_INIT &&
- m_editors[MODE_INIT]->text ().empty ()) {
- /* If it is init mode, and no any previouse input text,
- * we will let client applications to handle release key event */
- return FALSE;
- }
- else {
- return TRUE;
- }
- }
-
- /* Toggle simp/trad Chinese Mode when hotkey Ctrl + Shift + F pressed */
- if (keyval == IBUS_F && scmshm_test (modifiers, (IBUS_SHIFT_MASK | IBUS_CONTROL_MASK))) {
- m_props.toggleModeSimp ();
- m_prev_pressed_key = IBUS_F;
- return TRUE;
- }
-
- if (m_props.modeChinese ()) {
- if (m_input_mode == MODE_INIT &&
- ((cmshm_filter (modifiers)) == 0)) {
- const String & text = m_editors[MODE_INIT]->text ();
- if (text.empty ()) {
- switch (keyval) {
- case IBUS_grave:
- m_input_mode = MODE_PUNCT;
- break;
-#ifdef IBUS_BUILD_LUA_EXTENSION
- case IBUS_i:
- // do not enable lua extension when use double pinyin.
- if (PinyinConfig::instance ().doublePinyin ())
- break;
- m_input_mode = MODE_EXTENSION;
- break;
-#endif
-#ifdef IBUS_BUILD_ENGLISH_INPUT_MODE
- case IBUS_v:
- // do not enable english mode when use double pinyin.
- if (PinyinConfig::instance ().doublePinyin ())
- break;
- m_input_mode = MODE_ENGLISH;
- break;
-#endif
- }
- }
- else {
- if (m_prev_pressed_key != IBUS_period) {
- if ((keyval == IBUS_at || keyval == IBUS_colon)) {
- m_input_mode = MODE_RAW;
- m_editors[MODE_RAW]->setText (text, text.length ());
- m_editors[MODE_INIT]->reset ();
- }
- }
- else {
- if ((keyval >= IBUS_a && keyval <= IBUS_z) ||
- (keyval >= IBUS_A && keyval <= IBUS_Z)) {
- String tmp = text;
- tmp += ".";
- m_input_mode = MODE_RAW;
- m_editors[MODE_RAW]->setText (tmp, tmp.length ());
- m_editors[MODE_INIT]->reset ();
- }
- }
- }
- }
- retval = m_editors[m_input_mode]->processKeyEvent (keyval, keycode, modifiers);
- if (G_UNLIKELY (retval &&
- m_input_mode != MODE_INIT &&
- m_editors[m_input_mode]->text ().empty ()))
- m_input_mode = MODE_INIT;
- }
-
- if (G_UNLIKELY (!retval))
- retval = m_fallback_editor->processKeyEvent (keyval, keycode, modifiers);
-
- /* store ignored key event by editors */
- m_prev_pressed_key = retval ? IBUS_VoidSymbol : keyval;
-
- return retval;
-}
-
-void
-PinyinEngine::focusIn (void)
-{
- if (PinyinConfig::instance ().doublePinyin ()) {
- if (dynamic_cast <DoublePinyinEditor *> (m_editors[MODE_INIT].get ()) == NULL) {
- m_editors[MODE_INIT].reset (new DoublePinyinEditor (m_props, PinyinConfig::instance ()));
- connectEditorSignals (m_editors[MODE_INIT]);
- }
- }
- else {
- if (dynamic_cast <FullPinyinEditor *> (m_editors[MODE_INIT].get ()) == NULL) {
- m_editors[MODE_INIT].reset (new FullPinyinEditor (m_props, PinyinConfig::instance ()));
- connectEditorSignals (m_editors[MODE_INIT]);
- }
- }
- registerProperties (m_props.properties ());
-}
-
-
-void
-PinyinEngine::focusOut (void)
-{
- reset ();
-}
-
-void
-PinyinEngine::reset (void)
-{
- m_prev_pressed_key = IBUS_VoidSymbol;
- m_input_mode = MODE_INIT;
- for (gint i = 0; i < MODE_LAST; i++) {
- m_editors[i]->reset ();
- }
- m_fallback_editor->reset ();
-}
-
-void
-PinyinEngine::enable (void)
-{
- m_props.reset ();
-}
-
-void
-PinyinEngine::disable (void)
-{
-}
-
-void
-PinyinEngine::pageUp (void)
-{
- m_editors[m_input_mode]->pageUp ();
-}
-
-void
-PinyinEngine::pageDown (void)
-{
- m_editors[m_input_mode]->pageDown ();
-}
-
-void
-PinyinEngine::cursorUp (void)
-{
- m_editors[m_input_mode]->cursorUp ();
-}
-
-void
-PinyinEngine::cursorDown (void)
-{
- m_editors[m_input_mode]->cursorDown ();
-}
-
-inline void
-PinyinEngine::showSetupDialog (void)
-{
- g_spawn_command_line_async (LIBEXECDIR"/ibus-setup-pinyin pinyin", NULL);
-}
-
-gboolean
-PinyinEngine::propertyActivate (const gchar *prop_name, guint prop_state)
-{
- const static std::string setup ("setup");
- if (m_props.propertyActivate (prop_name, prop_state)) {
- return TRUE;
- }
- else if (setup == prop_name) {
- showSetupDialog ();
- return TRUE;
- }
- return FALSE;
-}
-
-void
-PinyinEngine::candidateClicked (guint index, guint button, guint state)
-{
- m_editors[m_input_mode]->candidateClicked (index, button, state);
-}
-
-void
-PinyinEngine::commitText (Text & text)
-{
- Engine::commitText (text);
- if (m_input_mode != MODE_INIT)
- m_input_mode = MODE_INIT;
- if (text.text ())
- static_cast<FallbackEditor*> (m_fallback_editor.get ())->setPrevCommittedChar (*text.text ());
- else
- static_cast<FallbackEditor*> (m_fallback_editor.get ())->setPrevCommittedChar (0);
-}
-
-void
-PinyinEngine::connectEditorSignals (EditorPtr editor)
-{
- editor->signalCommitText ().connect (
- std::bind (&PinyinEngine::commitText, this, _1));
-
- editor->signalUpdatePreeditText ().connect (
- std::bind (&PinyinEngine::updatePreeditText, this, _1, _2, _3));
- editor->signalShowPreeditText ().connect (
- std::bind (&PinyinEngine::showPreeditText, this));
- editor->signalHidePreeditText ().connect (
- std::bind (&PinyinEngine::hidePreeditText, this));
-
- editor->signalUpdateAuxiliaryText ().connect (
- std::bind (&PinyinEngine::updateAuxiliaryText, this, _1, _2));
- editor->signalShowAuxiliaryText ().connect (
- std::bind (&PinyinEngine::showAuxiliaryText, this));
- editor->signalHideAuxiliaryText ().connect (
- std::bind (&PinyinEngine::hideAuxiliaryText, this));
-
- editor->signalUpdateLookupTable ().connect (
- std::bind (&PinyinEngine::updateLookupTable, this, _1, _2));
- editor->signalUpdateLookupTableFast ().connect (
- std::bind (&PinyinEngine::updateLookupTableFast, this, _1, _2));
- editor->signalShowLookupTable ().connect (
- std::bind (&PinyinEngine::showLookupTable, this));
- editor->signalHideLookupTable ().connect (
- std::bind (&PinyinEngine::hideLookupTable, this));
-}
-
-};
-
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __PY_PINYIN_ENGINE_H_
-#define __PY_PINYIN_ENGINE_H_
-
-#include "PYEngine.h"
-#include "PYPinyinProperties.h"
-
-namespace PY {
-
-class PinyinEngine : public Engine {
-public:
- PinyinEngine (IBusEngine *engine);
- ~PinyinEngine (void);
-
- // virtual functions
- gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers);
- void focusIn (void);
- void focusOut (void);
- void reset (void);
- void enable (void);
- void disable (void);
- void pageUp (void);
- void pageDown (void);
- void cursorUp (void);
- void cursorDown (void);
- gboolean propertyActivate (const gchar *prop_name, guint prop_state);
- void candidateClicked (guint index, guint button, guint state);
-
-private:
- gboolean processPunct (guint keyval, guint keycode, guint modifiers);
-
-private:
- void showSetupDialog (void);
- void connectEditorSignals (EditorPtr editor);
-
-private:
- void commitText (Text & text);
-
-private:
- PinyinProperties m_props;
-
- guint m_prev_pressed_key;
-
- enum {
- MODE_INIT = 0, // init mode
- MODE_PUNCT, // punct mode
- MODE_RAW, // raw mode
- MODE_ENGLISH, // press v into English input mode
- #if 0
- MODE_STROKE, // press u into stroke input mode
- #endif
- MODE_EXTENSION, // press i into extension input mode
- MODE_LAST,
- } m_input_mode;
-
- EditorPtr m_editors[MODE_LAST];
- EditorPtr m_fallback_editor;
-};
-
-};
-
-#endif
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <cstring>
-#include <cstdlib>
-#include "PYPinyinParser.h"
-
-namespace PY {
-
-#include "PYBopomofo.h"
-#include "PYPinyinParserTable.h"
-
-static gboolean
-check_flags (const Pinyin *pinyin, guint option)
-{
- if (pinyin == NULL)
- return FALSE;
-
- if (pinyin->flags != 0) {
- guint flags;
- flags = pinyin->flags & option;
- if (flags == 0)
- return FALSE;
- if ((flags != pinyin->flags) && ((pinyin->flags & PINYIN_CORRECT_ALL) != 0))
- return FALSE;
- }
- return TRUE;
-}
-
-static int
-py_cmp (const void *p1, const void *p2)
-{
- const gchar *str = (const gchar *) p1;
- const Pinyin *py = (const Pinyin *) p2;
-
- return std::strcmp (str, py->text);
-}
-
-static const Pinyin *
-is_pinyin (const gchar *p,
- const gchar *end,
- gint len,
- guint option)
-{
- gchar buf[8];
- const Pinyin *result;
-
- if (G_UNLIKELY (len > 6))
- return NULL;
-
- if (G_UNLIKELY (len > end - p))
- return NULL;
-
- if (G_LIKELY (len > 0)) {
- std::strncpy (buf, p, len);
- buf[len] = 0;
- result = (const Pinyin *) std::bsearch (buf, pinyin_table, G_N_ELEMENTS (pinyin_table),
- sizeof (Pinyin), py_cmp);
- if (check_flags (result, option))
- return result;
- return NULL;
- }
-
- /* len < 0 */
- len = MIN (6, end - p);
- std::strncpy (buf, p, len);
-
- for (; len > 0; len --) {
- buf[len] = 0;
- result = (const Pinyin *) std::bsearch (buf, pinyin_table, G_N_ELEMENTS (pinyin_table),
- sizeof (Pinyin), py_cmp);
- if (G_UNLIKELY (check_flags (result, option))) {
- return result;
- }
- }
-
- return NULL;
-}
-
-static int
-sp_cmp (const void *p1,
- const void *p2)
-{
- const Pinyin **pys = (const Pinyin **) p1;
- const Pinyin **e = (const Pinyin **) p2;
-
- int retval = pys[0] - e[0];
-
- if (retval != 0)
- return retval;
- return pys[1] - e[1];
-}
-
-static const Pinyin **
-need_resplit(const Pinyin *p1,
- const Pinyin *p2)
-{
- const Pinyin * pys[] = {p1, p2};
-
- return (const Pinyin **) std::bsearch (pys, special_table, G_N_ELEMENTS (special_table),
- sizeof (special_table[0]), sp_cmp);
-}
-
-guint
-PinyinParser::parse (const String &pinyin,
- gint len,
- guint option,
- PinyinArray &result,
- guint max)
-{
-
- const gchar *p;
- const gchar *end;
- const Pinyin *py;
- const Pinyin *prev_py;
- gchar prev_c;
-
- result.clear ();
-
- if (G_UNLIKELY (len < 0))
- len = pinyin.size ();
-
- p = pinyin;
- end = p + len;
-
- prev_py = NULL;
-
- prev_c = 0;
- for (; p < end && result.size () < max; ) {
- if (G_UNLIKELY (*p == '\'')) {
- prev_c = '\'';
- p++;
- continue;
- }
- switch (prev_c) {
- case 'r':
- case 'n':
- case 'g':
- case 'e':
- switch (*p) {
- case 'i':
- case 'u':
- case 'v':
- case 'a':
- case 'e':
- case 'o':
- case 'r':
- {
- const Pinyin **pp;
- const Pinyin *new_py1;
- const Pinyin *new_py2;
-
- py = is_pinyin (p, end, -1, option);
-
- if ((new_py1 = is_pinyin (prev_py->text,
- prev_py->text + prev_py->len,
- prev_py->len - 1,
- option)) != NULL) {
- new_py2 = is_pinyin (p -1, end, -1, option);
-
- if (((new_py2 != NULL) && (new_py2->len > 1 )) &&
- (py == NULL || new_py2->len > py->len + 1)) {
- PinyinSegment & segment = result[result.size () - 1];
- segment.pinyin = new_py1;
- segment.len = new_py1->len;
- py = new_py2;
- p --;
- break;
- }
- }
-
- if ( py == NULL)
- break;
-
- pp = need_resplit (prev_py, py);
- if (pp != NULL) {
- PinyinSegment & segment = result[result.size () - 1];
- segment.pinyin = pp[2];
- segment.len = pp[2]->len;
- py = pp[3];
- p --;
- break;
- }
- }
- default:
- py = is_pinyin (p, end, -1, option);
- break;
- }
- break;
- default:
- py = is_pinyin (p, end, -1, option);
- break;
- }
-
- if (G_UNLIKELY (py == NULL))
- break;
-
- result.append (py, p - (const gchar *) pinyin, py->len);
- p += py->len;
- prev_c = py->text[py->len - 1];
- prev_py = py;
- }
-
- if (G_UNLIKELY (p == (const gchar *)pinyin))
- return 0;
-#if 0
- if (G_UNLIKELY (*(p - 1) == '\''))
- p --;
-#endif
- return p - (const gchar *)pinyin;
-}
-
-static const gchar * const
-id_map[] = {
- "", "b", "c", "ch",
- "d", "f", "g", "h",
- "j", "k", "l", "m",
- "n", "p", "q", "r",
- "s", "sh", "t", "w",
- "x", "y", "z", "zh",
- "a", "ai", "an", "ang", "ao",
- "e", "ei", "en", "eng", "er",
- "i", "ia", "ian", "iang", "iao",
- "ie", "in", "ing", "iong", "iu",
- "o", "ong", "ou",
- "u", "ua", "uai", "uan", "uang",
- 0, /* it should be ue or ve */
- "ui", "un", "uo", "v"
-};
-
-const Pinyin *
-PinyinParser::isPinyin (gint sheng, gint yun, guint option)
-{
- const Pinyin *result;
- gchar buf[16];
-
- std::strcpy (buf, id_map[sheng]);
-
- if (yun == PINYIN_ID_UE) {
- /* append ue or ve base on sheng */
- switch (sheng) {
- case PINYIN_ID_J:
- case PINYIN_ID_Q:
- case PINYIN_ID_X:
- case PINYIN_ID_Y:
- std::strcat (buf, "ue");
- break;
- default:
- std::strcat (buf, "ve");
- break;
- }
- }
- else {
- std::strcat (buf, id_map[yun]);
- }
-
- result = (const Pinyin *) bsearch (buf, pinyin_table, G_N_ELEMENTS (pinyin_table),
- sizeof (Pinyin), py_cmp);
- if (check_flags (result, option))
- return result;
- return NULL;
-}
-
-static int
-bopomofo_cmp (const void *p1, const void *p2)
-{
- const wchar_t *s1 = (wchar_t *) p1;
- const Pinyin *s2 = *(const Pinyin **) p2;
-
- return std::wcscmp (s1, s2->bopomofo);
-}
-
-gboolean
-PinyinParser::isBopomofoToneChar (const wchar_t ch)
-{
- return ch == bopomofo_char[BOPOMOFO_TONE_2]
- || ch == bopomofo_char[BOPOMOFO_TONE_3]
- || ch == bopomofo_char[BOPOMOFO_TONE_4]
- || ch == bopomofo_char[BOPOMOFO_TONE_5];
-}
-
-guint
-PinyinParser::parseBopomofo (const std::wstring &bopomofo,
- gint len,
- guint option,
- PinyinArray &result,
- guint max)
-{
- std::wstring::const_iterator bpmf = bopomofo.begin();
- const std::wstring::const_iterator end = bpmf + len;
- const Pinyin **bs_res;
- wchar_t buf[MAX_BOPOMOFO_LEN + 1];
- guint i, j;
-
- result.clear ();
-
- if (G_UNLIKELY (len < 0))
- len = bopomofo.length ();
-
- for (; bpmf < end && result.size () < max;) {
- for (i = MAX_BOPOMOFO_LEN; i > 0; i--){
- if (bpmf + i > end)
- continue;
-
- for (j = 0; j < i; j++){
- wchar_t key = *(bpmf + j);
-
- if (j == i - 1 && isBopomofoToneChar (key)) {
- break; /* ignore tone */
- }
-
- buf[j] = key;
- }
-
- buf[j] = '\0';
- bs_res = (const Pinyin **) std::bsearch (buf,
- bopomofo_table,
- G_N_ELEMENTS (bopomofo_table),
- sizeof (bopomofo_table[0]),
- bopomofo_cmp);
- if (bs_res != NULL && check_flags (*bs_res, option))
- break;
- }
- if (!(bs_res != NULL && check_flags (*bs_res, option)))
- break;
-
- result.append(*bs_res, bpmf - bopomofo.begin (), i);
- bpmf += i;
- }
-
- return bpmf - bopomofo.begin ();
-};
-
-};
+++ /dev/null
-/* vim:set et ts=4 sts=4:
- *
- * ibus-pinyin - The Chinese PinYin engine for IBus
- *
- * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@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 2, 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, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#ifndef __PY_PARSER_H_
-#define __PY_PARSER_H_
-
-#include <glib.h>
-#include "PYString.h"
-#include "PYPinyinArray.h"
-
-namespace PY {
-
-class PinyinParser {
-public:
- static guint parse (const String &pinyin, // pinyin string
- gint len, // length of pinyin string
- guint option, // option
- PinyinArray &result, // store pinyin in result
- guint max); // max length of the result
- static const Pinyin * isPinyin (gint sheng, gint yun, guint option);
- static guint parseBopomofo (const std::wstring &bopomofo,
- gint len,
- guint option,
- PinyinArray &result,
- guint max);
- static gboolean isBopomofoToneChar (const wchar_t ch);
-
-};
-
-};
-#endif