dd56fa731478dd52998f39de727ecf2d4755c7bf
[platform/upstream/ibus-libpinyin.git] / src / PYLibPinyin.cc
1 /* vim:set et ts=4 sts=4:
2  *
3  * ibus-libpinyin - Intelligent Pinyin engine based on libpinyin for IBus
4  *
5  * Copyright (c) 2011 Peng Wu <alexepico@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21
22 #include "PYLibPinyin.h"
23 #include <pinyin.h>
24 #include "PYPConfig.h"
25
26 #define LIBPINYIN_SAVE_TIMEOUT   (5 * 60)
27
28 using namespace PY;
29
30 std::unique_ptr<LibPinyinBackEnd> LibPinyinBackEnd::m_instance;
31
32 static LibPinyinBackEnd libpinyin_backend;
33
34 LibPinyinBackEnd::LibPinyinBackEnd () {
35     m_timeout_id = 0;
36     m_timer = g_timer_new();
37     m_pinyin_context = NULL;
38     m_chewing_context = NULL;
39 }
40
41 LibPinyinBackEnd::~LibPinyinBackEnd () {
42     g_timer_destroy (m_timer);
43     if (m_timeout_id != 0) {
44         saveUserDB ();
45         g_source_remove (m_timeout_id);
46     }
47
48     if (m_pinyin_context)
49         pinyin_fini(m_pinyin_context);
50     m_pinyin_context = NULL;
51     if (m_chewing_context)
52         pinyin_fini(m_chewing_context);
53     m_chewing_context = NULL;
54 }
55
56 pinyin_instance_t *
57 LibPinyinBackEnd::allocPinyinInstance ()
58 {
59     if (NULL == m_pinyin_context) {
60         gchar * userdir = g_build_filename (g_get_home_dir(), ".cache",
61                                             "ibus", "libpinyin", NULL);
62         int retval = g_mkdir_with_parents (userdir, 0700);
63         if (retval) {
64             g_free(userdir); userdir = NULL;
65         }
66         m_pinyin_context = pinyin_init ("/usr/share/libpinyin/data", userdir);
67         g_free(userdir);
68     }
69
70     setPinyinOptions (&LibPinyinPinyinConfig::instance ());
71     return pinyin_alloc_instance (m_pinyin_context);
72 }
73
74 void
75 LibPinyinBackEnd::freePinyinInstance (pinyin_instance_t *instance)
76 {
77     pinyin_free_instance (instance);
78 }
79
80 pinyin_instance_t *
81 LibPinyinBackEnd::allocChewingInstance ()
82 {
83     if (NULL == m_chewing_context) {
84         gchar * userdir = g_build_filename (g_get_home_dir(), ".cache",
85                                             "ibus", "libbopomofo", NULL);
86         int retval = g_mkdir_with_parents (userdir, 0700);
87         if (retval) {
88             g_free(userdir); userdir = NULL;
89         }
90         m_chewing_context = pinyin_init ("/usr/share/libpinyin/data", NULL);
91         g_free(userdir);
92     }
93
94     setChewingOptions (&LibPinyinBopomofoConfig::instance ());
95     return pinyin_alloc_instance (m_chewing_context);
96 }
97
98 void
99 LibPinyinBackEnd::freeChewingInstance (pinyin_instance_t *instance)
100 {
101     pinyin_free_instance (instance);
102 }
103
104 void
105 LibPinyinBackEnd::init (void) {
106     g_assert (NULL == m_instance.get ());
107     LibPinyinBackEnd * backend = new LibPinyinBackEnd;
108     m_instance.reset (backend);
109 }
110
111 void
112 LibPinyinBackEnd::finalize (void) {
113     m_instance.reset ();
114 }
115
116 /* Here are the double pinyin keyboard scheme mapping table. */
117 static const struct{
118     gint double_pinyin_keyboard;
119     DoublePinyinScheme scheme;
120 } double_pinyin_options [] = {
121     {0, DOUBLE_PINYIN_MS},
122     {1, DOUBLE_PINYIN_ZRM},
123     {2, DOUBLE_PINYIN_ABC},
124     {3, DOUBLE_PINYIN_ZIGUANG},
125     {4, DOUBLE_PINYIN_PYJJ},
126     {5, DOUBLE_PINYIN_XHE}
127 };
128
129 gboolean
130 LibPinyinBackEnd::setPinyinOptions (Config *config)
131 {
132     if (NULL == m_pinyin_context)
133         return FALSE;
134
135     const gint map = config->doublePinyinSchema ();
136     for (guint i = 0; i < G_N_ELEMENTS (double_pinyin_options); i++) {
137         if (map == double_pinyin_options[i].double_pinyin_keyboard) {
138             /* set double pinyin scheme. */
139             DoublePinyinScheme scheme = double_pinyin_options[i].scheme;
140             pinyin_set_double_pinyin_scheme (m_pinyin_context, scheme);
141         }
142     }
143
144     pinyin_option_t options = config->option() | USE_RESPLIT_TABLE;
145     pinyin_set_options (m_pinyin_context, options);
146     return TRUE;
147 }
148
149 /* Here are the chewing keyboard scheme mapping table. */
150 static const struct {
151     gint bopomofo_keyboard;
152     ChewingScheme scheme;
153 } chewing_options [] = {
154     {0, CHEWING_STANDARD},
155     {1, CHEWING_GINYIEH},
156     {2, CHEWING_ETEN},
157     {3, CHEWING_IBM}
158 };
159
160
161 gboolean
162 LibPinyinBackEnd::setChewingOptions (Config *config)
163 {
164     if (NULL == m_chewing_context)
165         return FALSE;
166
167     const gint map = config->bopomofoKeyboardMapping ();
168     for (guint i = 0; i < G_N_ELEMENTS (chewing_options); i++) {
169         if (map == chewing_options[i].bopomofo_keyboard) {
170             /* TODO: set chewing scheme. */
171             ChewingScheme scheme = chewing_options[i].scheme;
172             pinyin_set_chewing_scheme (m_chewing_context, scheme);
173         }
174     }
175
176     pinyin_option_t options = config->option() | USE_TONE;
177     pinyin_set_options(m_chewing_context, options);
178     return TRUE;
179 }
180
181 void
182 LibPinyinBackEnd::modified (void)
183 {
184     /* Restart the timer */
185     g_timer_start (m_timer);
186
187     if (m_timeout_id != 0)
188         return;
189
190     m_timeout_id = g_timeout_add_seconds (LIBPINYIN_SAVE_TIMEOUT,
191                                           LibPinyinBackEnd::timeoutCallback,
192                                           static_cast<gpointer> (this));
193 }
194
195 gboolean
196 LibPinyinBackEnd::timeoutCallback (gpointer data)
197 {
198     LibPinyinBackEnd *self = static_cast<LibPinyinBackEnd *> (data);
199
200     /* Get the elapsed time since last modification of database. */
201     guint elapsed = (guint)g_timer_elapsed (self->m_timer, NULL);
202
203     if (elapsed >= LIBPINYIN_SAVE_TIMEOUT &&
204         self->saveUserDB ()) {
205         self->m_timeout_id = 0;
206         return FALSE;
207     }
208
209     return TRUE;
210 }
211
212 gboolean
213 LibPinyinBackEnd::saveUserDB (void)
214 {
215     if (m_pinyin_context)
216         pinyin_save (m_pinyin_context);
217     if (m_chewing_context)
218         pinyin_save (m_chewing_context);
219     return TRUE;
220 }