fixes compile
[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
24 #include <string.h>
25 #include <pinyin.h>
26 #include "PYPConfig.h"
27
28 #define LIBPINYIN_SAVE_TIMEOUT   (5 * 60)
29
30 using namespace PY;
31
32 std::unique_ptr<LibPinyinBackEnd> LibPinyinBackEnd::m_instance;
33
34 static LibPinyinBackEnd libpinyin_backend;
35
36 LibPinyinBackEnd::LibPinyinBackEnd () {
37     m_timeout_id = 0;
38     m_timer = g_timer_new();
39     m_pinyin_context = NULL;
40     m_chewing_context = NULL;
41 }
42
43 LibPinyinBackEnd::~LibPinyinBackEnd () {
44     g_timer_destroy (m_timer);
45     if (m_timeout_id != 0) {
46         saveUserDB ();
47         g_source_remove (m_timeout_id);
48     }
49
50     if (m_pinyin_context)
51         pinyin_fini(m_pinyin_context);
52     m_pinyin_context = NULL;
53     if (m_chewing_context)
54         pinyin_fini(m_chewing_context);
55     m_chewing_context = NULL;
56 }
57
58 pinyin_context_t *
59 LibPinyinBackEnd::initPinyinContext (Config *config)
60 {
61     pinyin_context_t * context = NULL;
62
63     gchar * userdir = g_build_filename (g_get_user_cache_dir (),
64                                         "ibus", "libpinyin", NULL);
65     int retval = g_mkdir_with_parents (userdir, 0700);
66     if (retval) {
67         g_free(userdir); userdir = NULL;
68     }
69     context = pinyin_init (LIBPINYIN_DATADIR, userdir);
70     g_free (userdir);
71
72     const char *dicts = config->dictionaries ().c_str ();
73     gchar ** indices = g_strsplit_set (dicts, ";", -1);
74     for (size_t i = 0; i < g_strv_length(indices); ++i) {
75         int index = atoi (indices [i]);
76         if (index <= 1)
77             continue;
78
79         pinyin_load_phrase_library (context, index);
80     }
81     g_strfreev (indices);
82
83     /* load user phrase library. */
84     pinyin_load_phrase_library (context, USER_DICTIONARY);
85
86     return context;
87 }
88
89 pinyin_instance_t *
90 LibPinyinBackEnd::allocPinyinInstance ()
91 {
92     Config * config = &LibPinyinPinyinConfig::instance ();
93     if (NULL == m_pinyin_context) {
94         m_pinyin_context = initPinyinContext (config);
95     }
96
97     setPinyinOptions (config);
98     return pinyin_alloc_instance (m_pinyin_context);
99 }
100
101 void
102 LibPinyinBackEnd::freePinyinInstance (pinyin_instance_t *instance)
103 {
104     pinyin_free_instance (instance);
105 }
106
107 pinyin_context_t *
108 LibPinyinBackEnd::initChewingContext (Config *config)
109 {
110     pinyin_context_t * context = NULL;
111
112     gchar * userdir = g_build_filename (g_get_user_cache_dir (),
113                                         "ibus", "libbopomofo", NULL);
114     int retval = g_mkdir_with_parents (userdir, 0700);
115     if (retval) {
116         g_free(userdir); userdir = NULL;
117     }
118     context = pinyin_init (LIBPINYIN_DATADIR, userdir);
119     g_free(userdir);
120
121     const char *dicts = config->dictionaries ().c_str ();
122     gchar ** indices = g_strsplit_set (dicts, ";", -1);
123     for (size_t i = 0; i < g_strv_length(indices); ++i) {
124         int index = atoi (indices [i]);
125         if (index <= 1)
126             continue;
127
128         pinyin_load_phrase_library (context, index);
129     }
130     g_strfreev (indices);
131
132     return context;
133 }
134
135 pinyin_instance_t *
136 LibPinyinBackEnd::allocChewingInstance ()
137 {
138     Config *config = &LibPinyinBopomofoConfig::instance ();
139     if (NULL == m_chewing_context) {
140         m_chewing_context = initChewingContext (config);
141     }
142
143     setChewingOptions (config);
144     return pinyin_alloc_instance (m_chewing_context);
145 }
146
147 void
148 LibPinyinBackEnd::freeChewingInstance (pinyin_instance_t *instance)
149 {
150     pinyin_free_instance (instance);
151 }
152
153 void
154 LibPinyinBackEnd::init (void) {
155     g_assert (NULL == m_instance.get ());
156     LibPinyinBackEnd * backend = new LibPinyinBackEnd;
157     m_instance.reset (backend);
158 }
159
160 void
161 LibPinyinBackEnd::finalize (void) {
162     m_instance.reset ();
163 }
164
165 /* Here are the double pinyin keyboard scheme mapping table. */
166 static const struct{
167     gint double_pinyin_keyboard;
168     DoublePinyinScheme scheme;
169 } double_pinyin_options [] = {
170     {0, DOUBLE_PINYIN_MS},
171     {1, DOUBLE_PINYIN_ZRM},
172     {2, DOUBLE_PINYIN_ABC},
173     {3, DOUBLE_PINYIN_ZIGUANG},
174     {4, DOUBLE_PINYIN_PYJJ},
175     {5, DOUBLE_PINYIN_XHE}
176 };
177
178 gboolean
179 LibPinyinBackEnd::setPinyinOptions (Config *config)
180 {
181     if (NULL == m_pinyin_context)
182         return FALSE;
183
184     const gint map = config->doublePinyinSchema ();
185     for (guint i = 0; i < G_N_ELEMENTS (double_pinyin_options); i++) {
186         if (map == double_pinyin_options[i].double_pinyin_keyboard) {
187             /* set double pinyin scheme. */
188             DoublePinyinScheme scheme = double_pinyin_options[i].scheme;
189             pinyin_set_double_pinyin_scheme (m_pinyin_context, scheme);
190         }
191     }
192
193     pinyin_option_t options = config->option()
194         | USE_RESPLIT_TABLE | USE_DIVIDED_TABLE;
195     pinyin_set_options (m_pinyin_context, options);
196     return TRUE;
197 }
198
199 /* Here are the chewing keyboard scheme mapping table. */
200 static const struct {
201     gint bopomofo_keyboard;
202     ChewingScheme scheme;
203 } chewing_options [] = {
204     {0, CHEWING_STANDARD},
205     {1, CHEWING_GINYIEH},
206     {2, CHEWING_ETEN},
207     {3, CHEWING_IBM}
208 };
209
210
211 gboolean
212 LibPinyinBackEnd::setChewingOptions (Config *config)
213 {
214     if (NULL == m_chewing_context)
215         return FALSE;
216
217     const gint map = config->bopomofoKeyboardMapping ();
218     for (guint i = 0; i < G_N_ELEMENTS (chewing_options); i++) {
219         if (map == chewing_options[i].bopomofo_keyboard) {
220             /* TODO: set chewing scheme. */
221             ChewingScheme scheme = chewing_options[i].scheme;
222             pinyin_set_chewing_scheme (m_chewing_context, scheme);
223         }
224     }
225
226     pinyin_option_t options = config->option() | USE_TONE;
227     pinyin_set_options(m_chewing_context, options);
228     return TRUE;
229 }
230
231 void
232 LibPinyinBackEnd::modified (void)
233 {
234     /* Restart the timer */
235     g_timer_start (m_timer);
236
237     if (m_timeout_id != 0)
238         return;
239
240     m_timeout_id = g_timeout_add_seconds (LIBPINYIN_SAVE_TIMEOUT,
241                                           LibPinyinBackEnd::timeoutCallback,
242                                           static_cast<gpointer> (this));
243 }
244
245 gboolean
246 LibPinyinBackEnd::importPinyinDictionary (const char * filename)
247 {
248     /* user phrase library should be already loaded here. */
249     FILE * dictfile = fopen (filename, "r");
250     if (NULL == dictfile)
251         return FALSE;
252
253     import_iterator_t * iter = pinyin_begin_add_phrases
254         (m_pinyin_context, 15);
255
256     if (NULL == iter)
257         return FALSE;
258
259     char* linebuf = NULL; size_t size = 0; ssize_t read;
260     while ((read = getline (&linebuf, &size, dictfile)) != -1) {
261         if (0 == strlen (linebuf))
262             continue;
263
264         if ( '\n' == linebuf[strlen (linebuf) - 1] ) {
265             linebuf[strlen (linebuf) - 1] = '\0';
266         }
267
268         gchar ** items = g_strsplit_set (linebuf, " \t", 3);
269         guint len = g_strv_length (items);
270
271         gchar * phrase = NULL, * pinyin = NULL;
272         gint count = -1;
273         if (2 == len || 3 == len) {
274             phrase = items[0];
275             pinyin = items[1];
276             if (3 == len)
277                 count = atoi (items[2]);
278         } else
279             continue;
280
281         pinyin_iterator_add_phrase (iter, phrase, pinyin, count);
282     }
283
284     pinyin_end_add_phrases (iter);
285     fclose (dictfile);
286
287     pinyin_save (m_pinyin_context);
288     return TRUE;
289 }
290
291 gboolean
292 LibPinyinBackEnd::clearPinyinUserData (const char * target)
293 {
294     if (0 == strcmp ("all", target))
295         pinyin_mask_out (m_pinyin_context, 0x0, 0x0);
296     else if (0 == strcmp ("user", target))
297         pinyin_mask_out (m_pinyin_context, PHRASE_INDEX_LIBRARY_MASK,
298                         PHRASE_INDEX_MAKE_TOKEN (15, null_token));
299     else
300         g_warning ("unknown clear target: %s.\n", target);
301
302     pinyin_save (m_pinyin_context);
303     return TRUE;
304 }
305
306 gboolean
307 LibPinyinBackEnd::timeoutCallback (gpointer data)
308 {
309     LibPinyinBackEnd *self = static_cast<LibPinyinBackEnd *> (data);
310
311     /* Get the elapsed time since last modification of database. */
312     guint elapsed = (guint)g_timer_elapsed (self->m_timer, NULL);
313
314     if (elapsed >= LIBPINYIN_SAVE_TIMEOUT &&
315         self->saveUserDB ()) {
316         self->m_timeout_id = 0;
317         return FALSE;
318     }
319
320     return TRUE;
321 }
322
323 gboolean
324 LibPinyinBackEnd::saveUserDB (void)
325 {
326     if (m_pinyin_context)
327         pinyin_save (m_pinyin_context);
328     if (m_chewing_context)
329         pinyin_save (m_chewing_context);
330     return TRUE;
331 }