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