fixes compile
[platform/upstream/ibus-libpinyin.git] / src / PYPPhoneticEditor.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 "PYPPhoneticEditor.h"
22 #include "PYConfig.h"
23 #include "PYPinyinProperties.h"
24 #include "PYSimpTradConverter.h"
25
26 using namespace PY;
27
28 /* init static members */
29 LibPinyinPhoneticEditor::LibPinyinPhoneticEditor (PinyinProperties &props,
30                                                   Config &config):
31     Editor (props, config),
32     m_pinyin_len (0),
33     m_lookup_table (m_config.pageSize ())
34 {
35 }
36
37 LibPinyinPhoneticEditor::~LibPinyinPhoneticEditor (){
38 }
39
40 gboolean
41 LibPinyinPhoneticEditor::processSpace (guint keyval, guint keycode,
42                                        guint modifiers)
43 {
44     if (!m_text)
45         return FALSE;
46     if (cmshm_filter (modifiers) != 0)
47         return TRUE;
48
49     if (m_lookup_table.size () != 0) {
50         selectCandidate (m_lookup_table.cursorPos ());
51         update ();
52     }
53     else {
54         commit ();
55     }
56
57     return TRUE;
58 }
59
60 gboolean
61 LibPinyinPhoneticEditor::processFunctionKey (guint keyval, guint keycode, guint modifiers)
62 {
63     if (m_text.empty ())
64         return FALSE;
65
66     /* ignore numlock */
67     modifiers = cmshm_filter (modifiers);
68
69     if (modifiers != 0 && modifiers != IBUS_CONTROL_MASK)
70         return TRUE;
71
72     /* process some cursor control keys */
73     if (modifiers == 0) {  /* no modifiers. */
74         switch (keyval) {
75         case IBUS_Return:
76         case IBUS_KP_Enter:
77             commit (m_text.c_str ());
78             reset ();
79             return TRUE;
80
81         case IBUS_BackSpace:
82             removeCharBefore ();
83             return TRUE;
84
85         case IBUS_Delete:
86         case IBUS_KP_Delete:
87             removeCharAfter ();
88             return TRUE;
89
90         case IBUS_Left:
91         case IBUS_KP_Left:
92             moveCursorLeft ();
93             return TRUE;
94
95         case IBUS_Right:
96         case IBUS_KP_Right:
97             moveCursorRight ();
98             return TRUE;
99
100         case IBUS_Home:
101         case IBUS_KP_Home:
102             moveCursorToBegin ();
103             return TRUE;
104
105         case IBUS_End:
106         case IBUS_KP_End:
107             moveCursorToEnd ();
108             return TRUE;
109
110         case IBUS_Up:
111         case IBUS_KP_Up:
112             cursorUp ();
113             return TRUE;
114
115         case IBUS_Down:
116         case IBUS_KP_Down:
117             cursorDown ();
118             return TRUE;
119
120         case IBUS_Page_Up:
121         case IBUS_KP_Page_Up:
122             pageUp ();
123             return TRUE;
124
125         case IBUS_Page_Down:
126         case IBUS_KP_Page_Down:
127         case IBUS_Tab:
128             pageDown ();
129             return TRUE;
130
131         case IBUS_Escape:
132             reset ();
133             return TRUE;
134         default:
135             return TRUE;
136         }
137     } else { /* ctrl key pressed. */
138         switch (keyval) {
139         case IBUS_BackSpace:
140             removeWordBefore ();
141             return TRUE;
142
143         case IBUS_Delete:
144         case IBUS_KP_Delete:
145             removeWordAfter ();
146             return TRUE;
147
148         case IBUS_Left:
149         case IBUS_KP_Left:
150             moveCursorLeftByWord ();
151             return TRUE;
152
153         case IBUS_Right:
154         case IBUS_KP_Right:
155             moveCursorRightByWord ();
156             return TRUE;
157
158         default:
159             return TRUE;
160         }
161     }
162     return TRUE;
163 }
164
165 gboolean
166 LibPinyinPhoneticEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
167 {
168     return FALSE;
169 }
170
171 void
172 LibPinyinPhoneticEditor::updateLookupTableFast (void)
173 {
174     Editor::updateLookupTableFast (m_lookup_table, TRUE);
175 }
176
177 void
178 LibPinyinPhoneticEditor::updateLookupTable (void)
179 {
180     m_lookup_table.clear ();
181
182     fillLookupTableByPage ();
183     if (m_lookup_table.size()) {
184         Editor::updateLookupTable (m_lookup_table, TRUE);
185     } else {
186         hideLookupTable ();
187     }
188 }
189
190 gboolean
191 LibPinyinPhoneticEditor::fillLookupTableByPage (void)
192 {
193     guint len = 0;
194     pinyin_get_n_candidate (m_instance, &len);
195
196     guint filled_nr = m_lookup_table.size ();
197     guint page_size = m_lookup_table.pageSize ();
198
199     /* fill lookup table by libpinyin get candidates. */
200     guint need_nr = MIN (page_size, len - filled_nr);
201     g_assert (need_nr >=0);
202     if (need_nr == 0)
203         return FALSE;
204
205     String word;
206     for (guint i = filled_nr; i < filled_nr + need_nr; i++) {
207         if (i >= len)  /* no more candidates */
208             break;
209
210         lookup_candidate_t * candidate = NULL;
211         pinyin_get_candidate (m_instance, i, &candidate);
212
213         const gchar * phrase_string = NULL;
214         pinyin_get_candidate_string (m_instance, candidate, &phrase_string);
215
216         /* show get candidates. */
217         if (G_LIKELY (m_props.modeSimp ())) {
218             word = phrase_string;
219         } else { /* Traditional Chinese */
220             word.truncate (0);
221             SimpTradConverter::simpToTrad (phrase_string, word);
222         }
223
224         Text text (word);
225         m_lookup_table.appendCandidate (text);
226     }
227
228     return TRUE;
229 }
230
231 void
232 LibPinyinPhoneticEditor::pageUp (void)
233 {
234     if (G_LIKELY (m_lookup_table.pageUp ())) {
235         updateLookupTableFast ();
236         updatePreeditText ();
237         updateAuxiliaryText ();
238     }
239 }
240
241 void
242 LibPinyinPhoneticEditor::pageDown (void)
243 {
244     if (G_LIKELY((m_lookup_table.pageDown ()) ||
245                  (fillLookupTableByPage () && m_lookup_table.pageDown()))) {
246         updateLookupTableFast ();
247         updatePreeditText ();
248         updateAuxiliaryText ();
249     }
250 }
251
252 void
253 LibPinyinPhoneticEditor::cursorUp (void)
254 {
255     if (G_LIKELY (m_lookup_table.cursorUp ())) {
256         updateLookupTableFast ();
257         updatePreeditText ();
258         updateAuxiliaryText ();
259     }
260 }
261
262 void
263 LibPinyinPhoneticEditor::cursorDown (void)
264 {
265     if (G_LIKELY ((m_lookup_table.cursorPos () == m_lookup_table.size() - 1) &&
266                   (fillLookupTableByPage () == FALSE))) {
267         return;
268     }
269
270     if (G_LIKELY (m_lookup_table.cursorDown ())) {
271         updateLookupTableFast ();
272         updatePreeditText ();
273         updateAuxiliaryText ();
274     }
275 }
276
277 void
278 LibPinyinPhoneticEditor::candidateClicked (guint index, guint button, guint state)
279 {
280     selectCandidateInPage (index);
281 }
282
283 void
284 LibPinyinPhoneticEditor::reset (void)
285 {
286     m_pinyin_len = 0;
287     m_lookup_table.clear ();
288
289     pinyin_reset (m_instance);
290
291     Editor::reset ();
292 }
293
294 void
295 LibPinyinPhoneticEditor::update (void)
296 {
297     guint lookup_cursor = getLookupCursor ();
298     pinyin_guess_candidates (m_instance, lookup_cursor);
299
300     updateLookupTable ();
301     updatePreeditText ();
302     updateAuxiliaryText ();
303 }
304
305 void
306 LibPinyinPhoneticEditor::commit (const gchar *str)
307 {
308     StaticText text(str);
309     commitText (text);
310 }
311
312 guint
313 LibPinyinPhoneticEditor::getPinyinCursor ()
314 {
315     guint len = 0;
316     pinyin_get_n_pinyin (m_instance, &len);
317
318     /* Translate cursor position to pinyin position. */
319     guint pinyin_cursor = len;
320
321     guint16 prev_end = 0, cur_end;
322     for (size_t i = 0; i < len; ++i) {
323         PinyinKeyPos *pos = NULL;
324         pinyin_get_pinyin_key_rest (m_instance, i, &pos);
325         pinyin_get_pinyin_key_rest_positions (m_instance, pos, NULL, &cur_end);
326
327         if (prev_end <= m_cursor && m_cursor < cur_end)
328             pinyin_cursor = i;
329
330         prev_end = cur_end;
331     }
332
333     g_assert (pinyin_cursor >= 0);
334     return pinyin_cursor;
335 }
336
337 guint
338 LibPinyinPhoneticEditor::getLookupCursor (void)
339 {
340     guint len = 0;
341     pinyin_get_n_pinyin (m_instance, &len);
342     guint lookup_cursor = getPinyinCursor ();
343
344     /* show candidates when pinyin cursor is at end. */
345     if (lookup_cursor == len)
346         lookup_cursor = 0;
347     return lookup_cursor;
348 }
349
350 gboolean
351 LibPinyinPhoneticEditor::selectCandidate (guint i)
352 {
353     guint len = 0;
354     pinyin_get_n_candidate (m_instance, &len);
355
356     if (G_UNLIKELY (i >= len))
357         return FALSE;
358
359     guint lookup_cursor = getLookupCursor ();
360
361     lookup_candidate_t * candidate = NULL;
362     pinyin_get_candidate (m_instance, i, &candidate);
363
364     lookup_candidate_type_t type;
365     pinyin_get_candidate_type (m_instance, candidate, &type);
366
367     if (BEST_MATCH_CANDIDATE == type) {
368         commit ();
369         return TRUE;
370     }
371
372     lookup_cursor = pinyin_choose_candidate
373         (m_instance, lookup_cursor, candidate);
374
375     if (DIVIDED_CANDIDATE == type ||
376         RESPLIT_CANDIDATE == type) {
377         const gchar * str = NULL;
378         pinyin_get_raw_full_pinyin (m_instance, &str);
379
380         m_text = str;
381         updatePinyin ();
382     }
383     pinyin_guess_sentence (m_instance);
384
385     len = 0;
386     pinyin_get_n_pinyin (m_instance, &len);
387     if (lookup_cursor == len) {
388         pinyin_train(m_instance);
389         commit();
390         return TRUE;
391     }
392
393     PinyinKeyPos *pos = NULL;
394     pinyin_get_pinyin_key_rest (m_instance, lookup_cursor, &pos);
395
396     guint16 begin = 0;
397     pinyin_get_pinyin_key_rest_positions (m_instance, pos, &begin, NULL);
398     m_cursor = begin;
399
400     return TRUE;
401 }
402
403 gboolean
404 LibPinyinPhoneticEditor::selectCandidateInPage (guint i)
405 {
406     guint page_size = m_lookup_table.pageSize ();
407     guint cursor_pos = m_lookup_table.cursorPos ();
408
409     if (G_UNLIKELY (i >= page_size))
410         return FALSE;
411     i += (cursor_pos / page_size) * page_size;
412
413     return selectCandidate (i);
414 }
415
416 gboolean
417 LibPinyinPhoneticEditor::removeCharBefore (void)
418 {
419     if (G_UNLIKELY (m_cursor == 0))
420         return FALSE;
421
422     m_cursor --;
423     m_text.erase (m_cursor, 1);
424
425     updatePinyin ();
426     update ();
427
428     return TRUE;
429 }
430
431 gboolean
432 LibPinyinPhoneticEditor::removeCharAfter (void)
433 {
434     if (G_UNLIKELY (m_cursor == m_text.length ()))
435         return FALSE;
436
437     m_text.erase (m_cursor, 1);
438
439     updatePinyin ();
440     update ();
441
442     return TRUE;
443 }
444
445 gboolean
446 LibPinyinPhoneticEditor::moveCursorLeft (void)
447 {
448     if (G_UNLIKELY (m_cursor == 0))
449         return FALSE;
450
451     m_cursor --;
452     update ();
453     return TRUE;
454 }
455
456 gboolean
457 LibPinyinPhoneticEditor::moveCursorRight (void)
458 {
459     if (G_UNLIKELY (m_cursor == m_text.length ()))
460         return FALSE;
461
462     m_cursor ++;
463     update ();
464     return TRUE;
465 }
466
467 gboolean
468 LibPinyinPhoneticEditor::moveCursorToBegin (void)
469 {
470     if (G_UNLIKELY (m_cursor == 0))
471         return TRUE;
472
473     m_cursor = 0;
474     update ();
475     return TRUE;
476 }
477
478 gboolean
479 LibPinyinPhoneticEditor::moveCursorToEnd (void)
480 {
481     if (G_UNLIKELY (m_cursor == m_text.length ()))
482         return FALSE;
483
484     m_cursor = m_text.length ();
485     update ();
486     return TRUE;
487 }
488
489
490 /* move cursor by word functions */
491
492 guint
493 LibPinyinPhoneticEditor::getCursorLeftByWord (void)
494 {
495     guint16 cursor = 0;
496
497     if (G_UNLIKELY (m_cursor > m_pinyin_len)) {
498         cursor = m_pinyin_len;
499     } else {
500         guint len = 0;
501         pinyin_get_n_pinyin (m_instance, &len);
502
503         guint pinyin_cursor = getPinyinCursor ();
504
505         PinyinKeyPos *pos = NULL;
506
507         if (pinyin_cursor < len) {
508             pinyin_get_pinyin_key_rest (m_instance, pinyin_cursor, &pos);
509
510             pinyin_get_pinyin_key_rest_positions
511                 (m_instance, pos, &cursor, NULL);
512         } else {
513             /* at the end of pinyin string. */
514             cursor  = m_cursor;
515         }
516
517         /* cursor at the begin of one pinyin */
518         g_return_val_if_fail (pinyin_cursor > 0, 0);
519         if ( cursor == m_cursor) {
520             pinyin_get_pinyin_key_rest (m_instance, pinyin_cursor - 1, &pos);
521
522             pinyin_get_pinyin_key_rest_positions
523                 (m_instance, pos, &cursor, NULL);
524         }
525     }
526
527     return cursor;
528 }
529
530 guint
531 LibPinyinPhoneticEditor::getCursorRightByWord (void)
532 {
533     guint16 cursor = 0;
534
535     if (G_UNLIKELY (m_cursor > m_pinyin_len)) {
536         cursor = m_text.length ();
537     } else {
538         guint pinyin_cursor = getPinyinCursor ();
539         PinyinKeyPos *pos = NULL;
540         pinyin_get_pinyin_key_rest (m_instance, pinyin_cursor, &pos);
541         pinyin_get_pinyin_key_rest_positions (m_instance, pos, NULL, &cursor);
542     }
543
544     return cursor;
545 }
546
547 gboolean
548 LibPinyinPhoneticEditor::removeWordBefore (void)
549 {
550     if (G_UNLIKELY (m_cursor == 0))
551         return FALSE;
552
553     guint cursor = getCursorLeftByWord ();
554     m_text.erase (cursor, m_cursor - cursor);
555     m_cursor = cursor;
556     updatePinyin ();
557     update ();
558     return TRUE;
559 }
560
561 gboolean
562 LibPinyinPhoneticEditor::removeWordAfter (void)
563 {
564     if (G_UNLIKELY (m_cursor == m_text.length ()))
565         return FALSE;
566
567     guint cursor = getCursorRightByWord ();
568     m_text.erase (m_cursor, cursor - m_cursor);
569     updatePinyin ();
570     update ();
571     return TRUE;
572 }
573
574 gboolean
575 LibPinyinPhoneticEditor::moveCursorLeftByWord (void)
576 {
577     if (G_UNLIKELY (m_cursor == 0))
578         return FALSE;
579
580     guint cursor = getCursorLeftByWord ();
581
582     m_cursor = cursor;
583     update ();
584     return TRUE;
585 }
586
587 gboolean
588 LibPinyinPhoneticEditor::moveCursorRightByWord (void)
589 {
590     if (G_UNLIKELY (m_cursor == m_text.length ()))
591         return FALSE;
592
593     guint cursor = getCursorRightByWord ();
594
595     m_cursor = cursor;
596     update ();
597     return TRUE;
598 }