fixes compile
[platform/upstream/ibus-libpinyin.git] / src / PYPBopomofoEditor.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 #include "PYPBopomofoEditor.h"
22 #include "PYConfig.h"
23 #include "PYLibPinyin.h"
24 #include "PYPinyinProperties.h"
25 #include "PYSimpTradConverter.h"
26 #include "PYHalfFullConverter.h"
27
28
29 using namespace PY;
30
31 const static gchar * bopomofo_select_keys[] = {
32     "1234567890",
33     "asdfghjkl;",
34     "1qaz2wsxed",
35     "asdfzxcvgb",
36     "1234qweras",
37     "aoeu;qjkix",
38     "aoeuhtnsid",
39     "aoeuidhtns",
40     "qweasdzxcr"
41 };
42
43 LibPinyinBopomofoEditor::LibPinyinBopomofoEditor
44 (PinyinProperties & props, Config & config)
45     : LibPinyinPhoneticEditor (props, config),
46       m_select_mode (FALSE)
47 {
48     m_instance = LibPinyinBackEnd::instance ().allocChewingInstance ();
49 }
50
51 LibPinyinBopomofoEditor::~LibPinyinBopomofoEditor (void)
52 {
53     LibPinyinBackEnd::instance ().freeChewingInstance (m_instance);
54     m_instance = NULL;
55 }
56
57 void
58 LibPinyinBopomofoEditor::reset (void)
59 {
60     m_select_mode = FALSE;
61     LibPinyinPhoneticEditor::reset ();
62 }
63
64 gboolean
65 LibPinyinBopomofoEditor::insert (gint ch)
66 {
67     /* is full */
68     if (G_UNLIKELY (m_text.length () >= MAX_PINYIN_LEN))
69         return TRUE;
70
71     m_text.insert (m_cursor++, ch);
72     updatePinyin ();
73     update ();
74
75     return TRUE;
76 }
77
78
79 gboolean
80 LibPinyinBopomofoEditor::processGuideKey (guint keyval, guint keycode,
81                                           guint modifiers)
82 {
83     if (!m_config.guideKey ())
84         return FALSE;
85
86     if (G_UNLIKELY (cmshm_filter (modifiers) != 0))
87         return FALSE;
88
89     if (G_LIKELY (m_select_mode))
90         return FALSE;
91
92     if (G_UNLIKELY (keyval == IBUS_space)) {
93         m_select_mode = TRUE;
94         update ();
95         return TRUE;
96     }
97
98     return FALSE;
99 }
100
101 gboolean
102 LibPinyinBopomofoEditor::processAuxiliarySelectKey
103 (guint keyval, guint keycode, guint modifiers)
104 {
105     if (G_UNLIKELY (cmshm_filter (modifiers) != 0))
106         return FALSE;
107
108     guint i;
109
110     switch (keyval) {
111     case IBUS_KP_0:
112         i = 9;
113         if (!m_config.auxiliarySelectKeyKP ())
114             return FALSE;
115         break;
116     case IBUS_KP_1 ... IBUS_KP_9:
117         i = keyval - IBUS_KP_1;
118         if (!m_config.auxiliarySelectKeyKP ())
119             return FALSE;
120         break;
121     case IBUS_F1 ... IBUS_F10:
122         i = keyval - IBUS_F1;
123         if (!m_config.auxiliarySelectKeyF ())
124             return FALSE;
125         break;
126     default:
127         return FALSE;
128     }
129
130     m_select_mode = TRUE;
131     selectCandidateInPage (i);
132
133     update ();
134     return TRUE;
135 }
136
137 gboolean
138 LibPinyinBopomofoEditor::processSelectKey (guint keyval, guint keycode,
139                                            guint modifiers)
140 {
141     if (G_UNLIKELY (!m_text))
142         return FALSE;
143
144     if (G_LIKELY (!m_select_mode && ((modifiers & IBUS_MOD1_MASK) == 0)))
145         return FALSE;
146
147     const gchar * pos = NULL;
148     const gchar * keys = bopomofo_select_keys[m_config.selectKeys ()];
149     for ( const gchar * p = keys; *p; ++p ) {
150         if ( *p == keyval )
151             pos = p;
152     }
153
154     if (pos == NULL)
155         return FALSE;
156
157     m_select_mode = TRUE;
158
159     guint i = pos - keys;
160     selectCandidateInPage (i);
161
162     update ();
163     return TRUE;
164 }
165
166 gboolean
167 LibPinyinBopomofoEditor::processBopomofo (guint keyval, guint keycode,
168                                           guint modifiers)
169 {
170     if (G_UNLIKELY (cmshm_filter (modifiers) != 0))
171         return m_text ? TRUE : FALSE;
172
173     if (!(pinyin_in_chewing_keyboard (m_instance, keyval, NULL)))
174         return FALSE;
175
176     if (keyval == IBUS_space)
177         return FALSE;
178
179     m_select_mode = FALSE;
180
181     return insert (keyval);
182 }
183
184 gboolean
185 LibPinyinBopomofoEditor::processKeyEvent (guint keyval, guint keycode,
186                                           guint modifiers)
187 {
188     modifiers &= (IBUS_SHIFT_MASK |
189                   IBUS_CONTROL_MASK |
190                   IBUS_MOD1_MASK |
191                   IBUS_SUPER_MASK |
192                   IBUS_HYPER_MASK |
193                   IBUS_META_MASK |
194                   IBUS_LOCK_MASK);
195
196     if (G_UNLIKELY (processGuideKey (keyval, keycode, modifiers)))
197         return TRUE;
198     if (G_UNLIKELY (processSelectKey (keyval, keycode, modifiers)))
199         return TRUE;
200     if (G_UNLIKELY (processAuxiliarySelectKey (keyval, keycode,
201                                                modifiers)))
202         return TRUE;
203     if (G_LIKELY (processBopomofo (keyval, keycode, modifiers)))
204         return TRUE;
205
206     switch (keyval) {
207     case IBUS_space:
208         m_select_mode = TRUE;
209         return processSpace (keyval, keycode, modifiers);
210
211     case IBUS_Up:        case IBUS_KP_Up:
212     case IBUS_Down:      case IBUS_KP_Down:
213     case IBUS_Page_Up:   case IBUS_KP_Page_Up:
214     case IBUS_Page_Down: case IBUS_KP_Page_Down:
215     case IBUS_Tab:
216         m_select_mode = TRUE;
217         return LibPinyinPhoneticEditor::processFunctionKey
218             (keyval, keycode, modifiers);
219
220     case IBUS_BackSpace:
221     case IBUS_Delete:    case IBUS_KP_Delete:
222     case IBUS_Left:      case IBUS_KP_Left:
223     case IBUS_Right:     case IBUS_KP_Right:
224     case IBUS_Home:      case IBUS_KP_Home:
225     case IBUS_End:       case IBUS_KP_End:
226         m_select_mode = FALSE;
227         return LibPinyinPhoneticEditor::processFunctionKey
228             (keyval, keycode, modifiers);
229
230     default:
231         return LibPinyinPhoneticEditor::processFunctionKey
232             (keyval, keycode, modifiers);
233     }
234     return FALSE;
235 }
236
237 void
238 LibPinyinBopomofoEditor::updatePinyin (void)
239 {
240     if (G_UNLIKELY (m_text.empty ())) {
241         m_pinyin_len = 0;
242         /* TODO: check whether to replace "" with NULL. */
243         pinyin_parse_more_chewings (m_instance, "");
244         pinyin_guess_sentence (m_instance);
245         return;
246     }
247
248     m_pinyin_len =
249         pinyin_parse_more_chewings (m_instance, m_text.c_str ());
250     pinyin_guess_sentence (m_instance);
251 }
252
253 void
254 LibPinyinBopomofoEditor::commit ()
255 {
256     if (G_UNLIKELY (m_text.empty ()))
257         return;
258
259     m_buffer.clear ();
260
261     /* sentence candidate */
262     char *tmp = NULL;
263     pinyin_get_sentence (m_instance, &tmp);
264     if (tmp) {
265         if (m_props.modeSimp ()) {
266             m_buffer << tmp;
267         } else {
268             SimpTradConverter::simpToTrad (tmp, m_buffer);
269         }
270         g_free (tmp);
271     }
272
273     /* text after pinyin */
274     const gchar *p = m_text.c_str() + m_pinyin_len;
275     while (*p != '\0') {
276         const char * symbol = NULL;
277         if (pinyin_in_chewing_keyboard(m_instance, *p, &symbol)) {
278             m_buffer << symbol;
279         } else {
280             if (G_UNLIKELY (m_props.modeFull ())) {
281                 m_buffer.appendUnichar (HalfFullConverter::toFull (*p));
282             } else {
283                 m_buffer << *p;
284             }
285         }
286         ++p;
287     }
288
289     pinyin_train(m_instance);
290     LibPinyinBackEnd::instance ().modified();
291     LibPinyinPhoneticEditor::commit ((const gchar *)m_buffer);
292     reset();
293 }
294
295 void
296 LibPinyinBopomofoEditor::updatePreeditText ()
297 {
298     /* preedit text = guessed sentence + un-parsed pinyin text */
299     if (G_UNLIKELY (m_text.empty ())) {
300         hidePreeditText ();
301         return;
302     }
303
304     m_buffer.clear ();
305     char *tmp = NULL;
306     pinyin_get_sentence(m_instance, &tmp);
307     if (tmp) {
308         if (m_props.modeSimp ()) {
309             m_buffer<<tmp;
310         } else {
311             SimpTradConverter::simpToTrad (tmp, m_buffer);
312         }
313         g_free (tmp);
314         tmp = NULL;
315     }
316
317     /* append rest text */
318     const gchar *p = m_text.c_str () + m_pinyin_len;
319     m_buffer << p;
320
321     StaticText preedit_text (m_buffer);
322     /* underline */
323     preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
324
325     guint pinyin_cursor = getPinyinCursor ();
326     Editor::updatePreeditText (preedit_text, pinyin_cursor, TRUE);
327 }
328
329 void
330 LibPinyinBopomofoEditor::updateAuxiliaryText (void)
331 {
332     if (G_UNLIKELY (m_text.empty ())) {
333         hideAuxiliaryText ();
334         return;
335     }
336
337     m_buffer.clear ();
338
339     guint len = 0;
340     pinyin_get_n_pinyin (m_instance, &len);
341
342     for (guint i = 0; i < len; ++i) {
343         PinyinKey *key = NULL;
344         pinyin_get_pinyin_key (m_instance, i, &key);
345
346         PinyinKeyPos *pos = NULL;
347         pinyin_get_pinyin_key_rest (m_instance, i, &pos);
348
349         guint16 cursor = 0, end = 0;
350         pinyin_get_pinyin_key_rest_positions (m_instance, pos, &cursor, &end);
351
352         gchar * str = NULL;
353         if (G_UNLIKELY (cursor == m_cursor)) { /* at word boundary. */
354             pinyin_get_chewing_string (m_instance, key, &str);
355             m_buffer << '|' << str;
356             g_free (str);
357         } else if (G_LIKELY (cursor < m_cursor &&
358                              m_cursor < end)) { /* in word */
359             /* raw text */
360             guint16 length = 0;
361             pinyin_get_pinyin_key_rest_length (m_instance, pos, &length);
362
363             String raw = m_text.substr (cursor, length);
364             guint offset = m_cursor - cursor;
365             m_buffer << ' ';
366             String before = raw.substr (0, offset);
367             String after = raw.substr (offset);
368             String::const_iterator iter;
369             const char * symbol = NULL;
370             for ( iter = before.begin (); iter != before.end (); ++iter) {
371                 if ( pinyin_in_chewing_keyboard(m_instance, *iter, &symbol))
372                     m_buffer << symbol;
373                 else
374                     m_buffer << *iter;
375             }
376             m_buffer << '|';
377             for ( iter = after.begin (); iter != after.end (); ++iter) {
378                 if ( pinyin_in_chewing_keyboard (m_instance, *iter, &symbol))
379                     m_buffer << symbol;
380                 else
381                     m_buffer << *iter;
382             }
383         } else { /* other words */
384             pinyin_get_chewing_string (m_instance, key, &str);
385             m_buffer << ' ' << str;
386             g_free (str);
387         }
388     }
389
390     if (m_cursor == m_pinyin_len)
391         m_buffer << '|';
392
393     /* append rest text */
394     const gchar * p = m_text.c_str() + m_pinyin_len;
395     m_buffer << p;
396
397     StaticText aux_text (m_buffer);
398     Editor::updateAuxiliaryText (aux_text, TRUE);
399 }
400