clean db
[platform/upstream/ibus-libpinyin.git] / src / PYPBopomofoEditor.cc
1 /* vim:set et ts=4 sts=4:
2  *
3  * ibus-pinyin - The Chinese PinYin engine 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., 675 Mass Ave, Cambridge, MA 02139, 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         return;
245     }
246
247     m_pinyin_len =
248         pinyin_parse_more_chewings (m_instance, m_text.c_str ());
249     pinyin_guess_sentence (m_instance);
250 }
251
252 void
253 LibPinyinBopomofoEditor::commit ()
254 {
255     if (G_UNLIKELY (m_text.empty ()))
256         return;
257
258     m_buffer.clear ();
259
260     /* sentence candidate */
261     char *tmp = NULL;
262     pinyin_get_sentence(m_instance, &tmp);
263     if (tmp) {
264         if (m_props.modeSimp ()) {
265             m_buffer << tmp;
266         } else {
267             SimpTradConverter::simpToTrad (tmp, m_buffer);
268         }
269         g_free (tmp);
270     }
271
272     /* text after pinyin */
273     const gchar *p = m_text.c_str() + m_pinyin_len;
274     while (*p != '\0') {
275         const char * symbol = NULL;
276         if (pinyin_in_chewing_keyboard(m_instance, *p, &symbol)) {
277             m_buffer << symbol;
278         } else {
279             if (G_UNLIKELY (m_props.modeFull ())) {
280                 m_buffer.appendUnichar (HalfFullConverter::toFull (*p));
281             } else {
282                 m_buffer << *p;
283             }
284         }
285         ++p;
286     }
287
288     pinyin_train(m_instance);
289     LibPinyinBackEnd::instance ().modified();
290     LibPinyinPhoneticEditor::commit ((const gchar *)m_buffer);
291     reset();
292 }
293
294 void
295 LibPinyinBopomofoEditor::updatePreeditText ()
296 {
297     /* preedit text = guessed sentence + un-parsed pinyin text */
298     if (G_UNLIKELY (m_text.empty ())) {
299         hidePreeditText ();
300         return;
301     }
302
303     m_buffer.clear ();
304     char *tmp = NULL;
305     pinyin_get_sentence(m_instance, &tmp);
306     if (tmp) {
307         if (m_props.modeSimp ()) {
308             m_buffer<<tmp;
309         } else {
310             SimpTradConverter::simpToTrad (tmp, m_buffer);
311         }
312         g_free (tmp);
313         tmp = NULL;
314     }
315
316     /* append rest text */
317     const gchar *p = m_text.c_str () + m_pinyin_len;
318     m_buffer << p;
319
320     StaticText preedit_text (m_buffer);
321     /* underline */
322     preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
323
324     guint pinyin_cursor = getPinyinCursor ();
325     Editor::updatePreeditText (preedit_text, pinyin_cursor, TRUE);
326 }
327
328 void
329 LibPinyinBopomofoEditor::updateAuxiliaryText (void)
330 {
331     if (G_UNLIKELY (m_text.empty ())) {
332         hideAuxiliaryText ();
333         return;
334     }
335
336     m_buffer.clear ();
337
338     // guint pinyin_cursor = getPinyinCursor ();
339     PinyinKeyVector & pinyin_keys = m_instance->m_pinyin_keys;
340     PinyinKeyPosVector & pinyin_poses = m_instance->m_pinyin_key_rests;
341     for (guint i = 0; i < pinyin_keys->len; ++i) {
342         PinyinKey *key = &g_array_index (pinyin_keys, PinyinKey, i);
343         PinyinKeyPos *pos = &g_array_index (pinyin_poses, PinyinKeyPos, i);
344         guint cursor = pos->m_raw_begin;
345
346         if (G_UNLIKELY (cursor == m_cursor)) { /* at word boundary. */
347             m_buffer << '|' << key->get_chewing_string ();
348         } else if (G_LIKELY ( cursor < m_cursor &&
349                               m_cursor < pos->m_raw_end )) { /* in word */
350             /* raw text */
351             String raw = m_text.substr (cursor,
352                                         pos->length ());
353             guint offset = m_cursor - cursor;
354             m_buffer << ' ';
355             String before = raw.substr (0, offset);
356             String after = raw.substr (offset);
357             String::const_iterator iter;
358             const char * symbol = NULL;
359             for ( iter = before.begin (); iter != before.end (); ++iter) {
360                 if ( pinyin_in_chewing_keyboard(m_instance, *iter, &symbol))
361                     m_buffer << symbol;
362                 else
363                     m_buffer << *iter;
364             }
365             m_buffer << '|';
366             for ( iter = after.begin (); iter != after.end (); ++iter) {
367                 if ( pinyin_in_chewing_keyboard(m_instance, *iter, &symbol))
368                     m_buffer << symbol;
369                 else
370                     m_buffer << *iter;
371             }
372         } else { /* other words */
373             m_buffer << ' ' << key->get_chewing_string ();
374         }
375     }
376
377     if (m_cursor == m_pinyin_len)
378         m_buffer << '|';
379
380     /* append rest text */
381     const gchar * p = m_text.c_str() + m_pinyin_len;
382     m_buffer << p;
383
384     StaticText aux_text (m_buffer);
385     Editor::updateAuxiliaryText (aux_text, TRUE);
386 }
387