clean db
[platform/upstream/ibus-libpinyin.git] / src / PYPunctEditor.cc
1 /* vim:set et ts=4 sts=4:
2  *
3  * ibus-pinyin - The Chinese PinYin engine for IBus
4  *
5  * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
6  * Copyright (c) 2010 BYVoid <byvoid1@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 #include <algorithm>
23 #include "PYConfig.h"
24 #include "PYPunctEditor.h"
25
26 namespace PY {
27
28 #include "PYPunctTable.h"
29
30 PunctEditor::PunctEditor (PinyinProperties & props, Config & config)
31     : Editor (props, config),
32       m_punct_mode (MODE_DISABLE),
33       m_lookup_table (m_config.pageSize ())
34 {
35 }
36
37 gboolean
38 PunctEditor::insert (gchar ch)
39 {
40     switch (m_punct_mode) {
41     case MODE_DISABLE:
42         {
43             g_assert (ch == IBUS_grave);
44             g_assert (m_cursor == 0);
45             m_text.insert (m_cursor++, ch);
46             m_punct_mode = MODE_INIT;
47             updatePunctCandidates (0);
48             m_selected_puncts.clear ();
49             m_selected_puncts.insert (m_selected_puncts.begin (), m_punct_candidates[0]);
50             update ();
51         }
52         break;
53     case MODE_INIT:
54         {
55             m_text.clear ();
56             m_selected_puncts.clear ();
57             m_cursor = 0;
58         }
59     case MODE_NORMAL:
60         {
61             m_text.insert (m_cursor, ch);
62             updatePunctCandidates (ch);
63             m_punct_mode = MODE_NORMAL;
64             if (m_punct_candidates.size () > 0) {
65                 m_selected_puncts.insert (m_selected_puncts.begin () + m_cursor, m_punct_candidates[0]);
66             }
67             m_cursor ++;
68             update ();
69         }
70         break;
71     default:
72         g_assert_not_reached ();
73     }
74
75     return TRUE;
76 }
77
78 inline gboolean
79 PunctEditor::processSpace (guint keyval, guint keycode, guint modifiers)
80 {
81     if (m_punct_mode != MODE_INIT && m_punct_mode != MODE_NORMAL)
82         return FALSE;
83     if (cmshm_filter (modifiers) != 0)
84         return TRUE;
85     commit ();
86     return TRUE;
87 }
88
89 gboolean
90 PunctEditor::processPunct (guint keyval, guint keycode, guint modifiers)
91 {
92     if (cmshm_filter (modifiers) != 0)
93         return TRUE;
94
95     if (m_punct_mode == MODE_DISABLE) {
96         if (keyval == IBUS_grave) {
97             insert (keyval);
98             return TRUE;
99         }
100     }
101
102     g_assert (m_punct_mode == MODE_INIT || m_punct_mode == MODE_NORMAL);
103
104     switch (keyval) {
105     case IBUS_grave:        /* ` */
106     case IBUS_asciitilde:   /* ~ */
107     case IBUS_exclam:       /* ~ */
108     case IBUS_at:           /* @ */
109     case IBUS_numbersign:   /* # */
110     case IBUS_dollar:       /* $ */
111     case IBUS_percent:      /* % */
112     case IBUS_asciicircum:  /* ^ */
113     case IBUS_ampersand:    /* & */
114     case IBUS_asterisk:     /* * */
115     case IBUS_parenleft:    /* ( */
116     case IBUS_parenright:   /* ) */
117     case IBUS_minus:        /* - */
118     case IBUS_underscore:   /* _ */
119     case IBUS_equal:        /* = */
120     case IBUS_plus:         /* + */
121     case IBUS_bracketleft:  /* [ */
122     case IBUS_bracketright: /* ] */
123     case IBUS_braceleft:    /* { */
124     case IBUS_braceright:   /* } */
125     case IBUS_backslash:    /* \ */
126     case IBUS_bar:          /* | */
127     case IBUS_colon:        /* : */
128     case IBUS_semicolon:    /* ; */
129     case IBUS_apostrophe:   /* ' */
130     case IBUS_quotedbl:     /* " */
131     case IBUS_comma:        /* , */
132     case IBUS_period:       /* . */
133     case IBUS_less:         /* < */
134     case IBUS_greater:      /* > */
135     case IBUS_slash:        /* / */
136     case IBUS_question:     /* ? */
137     case IBUS_0...IBUS_9:
138     case IBUS_a...IBUS_z:
139     case IBUS_A...IBUS_Z:
140         return insert (keyval);
141     default:
142         return FALSE;
143     }
144 }
145
146 gboolean
147 PunctEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
148 {
149     modifiers &= (IBUS_SHIFT_MASK |
150                   IBUS_CONTROL_MASK |
151                   IBUS_MOD1_MASK |
152                   IBUS_SUPER_MASK |
153                   IBUS_HYPER_MASK |
154                   IBUS_META_MASK |
155                   IBUS_LOCK_MASK);
156
157     switch (keyval) {
158     case IBUS_space:
159         return processSpace (keyval, keycode, modifiers);
160
161     case IBUS_Return:
162     case IBUS_KP_Enter:
163         commit (m_text);
164         return TRUE;
165
166     case IBUS_Escape:
167         reset ();
168         return TRUE;
169
170     case IBUS_BackSpace:
171         removeCharBefore ();
172         return TRUE;
173
174     case IBUS_Delete:
175     case IBUS_KP_Delete:
176         removeCharAfter ();
177         return TRUE;
178
179     case IBUS_Left:
180     case IBUS_KP_Left:
181         moveCursorLeft ();
182         return TRUE;
183
184     case IBUS_Right:
185     case IBUS_KP_Right:
186         moveCursorRight ();
187         return TRUE;
188
189     case IBUS_Home:
190     case IBUS_KP_Home:
191         moveCursorToBegin ();
192         return TRUE;
193
194     case IBUS_End:
195     case IBUS_KP_End:
196         moveCursorToEnd ();
197         return TRUE;
198
199     case IBUS_Up:
200     case IBUS_KP_Up:
201         cursorUp ();
202         return TRUE;
203
204     case IBUS_Down:
205     case IBUS_KP_Down:
206         cursorDown ();
207         return TRUE;
208
209     case IBUS_Page_Up:
210     case IBUS_KP_Page_Up:
211         pageUp ();
212         return TRUE;
213
214     case IBUS_Page_Down:
215     case IBUS_KP_Page_Down:
216     case IBUS_Tab:
217         pageDown ();
218         return TRUE;
219     default:
220         return processPunct(keyval, keycode, modifiers);
221     }
222 }
223
224 void
225 PunctEditor::pageUp (void)
226 {
227     if (G_LIKELY (m_lookup_table.pageUp ())) {
228         m_selected_puncts[m_cursor - 1] = m_punct_candidates[m_lookup_table.cursorPos ()];
229         updateLookupTableFast (m_lookup_table, TRUE);
230         updatePreeditText ();
231         updateAuxiliaryText ();
232     }
233 }
234
235 void
236 PunctEditor::pageDown (void)
237 {
238     if (G_LIKELY (m_lookup_table.pageDown ())) {
239         m_selected_puncts[m_cursor - 1] = m_punct_candidates[m_lookup_table.cursorPos ()];
240         updateLookupTableFast (m_lookup_table, TRUE);
241         updatePreeditText ();
242         updateAuxiliaryText ();
243     }
244 }
245
246 void
247 PunctEditor::cursorUp (void)
248 {
249     if (G_LIKELY (m_lookup_table.cursorUp ())) {
250         m_selected_puncts[m_cursor - 1] = m_punct_candidates[m_lookup_table.cursorPos ()];
251         updateLookupTableFast (m_lookup_table, TRUE);
252         updatePreeditText ();
253         updateAuxiliaryText ();
254     }
255 }
256
257 void
258 PunctEditor::cursorDown (void)
259 {
260     if (G_LIKELY (m_lookup_table.cursorDown ())) {
261         m_selected_puncts[m_cursor - 1] = m_punct_candidates[m_lookup_table.cursorPos ()];
262         updateLookupTableFast (m_lookup_table, TRUE);
263         updatePreeditText ();
264         updateAuxiliaryText ();
265     }
266 }
267
268 gboolean
269 PunctEditor::moveCursorLeft (void)
270 {
271     if (G_UNLIKELY (m_cursor == 0))
272         return FALSE;
273     m_cursor --;
274     if (m_cursor == 0)  {
275         m_punct_candidates.clear ();
276         fillLookupTable ();
277     }
278     else {
279         updatePunctCandidates (m_text[m_cursor - 1]);
280         /* restore cursor pos */
281         std::vector<const gchar *>::iterator it;
282         it = std::find (m_punct_candidates.begin (),
283                         m_punct_candidates.end (),
284                         m_selected_puncts[m_cursor - 1]);
285         g_assert (it != m_punct_candidates.end ());
286         m_lookup_table.setCursorPos (it - m_punct_candidates.begin ());
287     }
288     update();
289     return TRUE;
290 }
291
292 gboolean
293 PunctEditor::moveCursorRight (void)
294 {
295     if (G_UNLIKELY (m_cursor == m_text.length ()))
296         return FALSE;
297     m_cursor ++;
298     updatePunctCandidates (m_text[m_cursor - 1]);
299
300     /* restore cursor pos */
301     std::vector<const gchar *>::iterator it;
302     it = std::find (m_punct_candidates.begin (),
303                     m_punct_candidates.end (),
304                     m_selected_puncts[m_cursor - 1]);
305     g_assert (it != m_punct_candidates.end ());
306     m_lookup_table.setCursorPos (it - m_punct_candidates.begin ());
307
308     update();
309     return TRUE;
310 }
311
312 gboolean
313 PunctEditor::moveCursorToBegin (void)
314 {
315     if (G_UNLIKELY (m_cursor == 0))
316         return FALSE;
317
318     g_assert (m_punct_mode == MODE_NORMAL);
319     m_cursor = 0;
320     m_punct_candidates.clear ();
321     fillLookupTable ();
322     update ();
323
324     return TRUE;
325 }
326
327 gboolean
328 PunctEditor::moveCursorToEnd (void)
329 {
330     if (G_UNLIKELY (m_cursor == m_text.length ()))
331         return FALSE;
332
333     g_assert (m_punct_mode == MODE_NORMAL);
334     m_cursor = m_text.length ();
335     updatePunctCandidates (m_text[m_cursor - 1]);
336
337     /* restore cursor pos */
338     std::vector<const gchar *>::iterator it;
339     it = std::find (m_punct_candidates.begin (),
340                     m_punct_candidates.end (),
341                     m_selected_puncts[m_cursor - 1]);
342     g_assert (it != m_punct_candidates.end ());
343     m_lookup_table.setCursorPos (it - m_punct_candidates.begin ());
344
345     update();
346     return TRUE;
347 }
348
349 gboolean
350 PunctEditor::removeCharBefore (void)
351 {
352     if (G_UNLIKELY (m_cursor == 0))
353         return FALSE;
354
355     m_cursor --;
356     m_selected_puncts.erase (m_selected_puncts.begin () + m_cursor);
357     m_text.erase (m_cursor, 1);
358     if (m_text.empty()) {
359         reset ();
360     }
361     else {
362         if (m_cursor > 0) {
363
364             updatePunctCandidates (m_text[m_cursor - 1]);
365             /* restore cursor pos */
366             std::vector<const gchar *>::iterator it;
367             it = std::find (m_punct_candidates.begin (),
368                             m_punct_candidates.end (),
369                             m_selected_puncts[m_cursor - 1]);
370             g_assert (it != m_punct_candidates.end ());
371             m_lookup_table.setCursorPos (it - m_punct_candidates.begin ());
372         }
373         else {
374             m_punct_candidates.clear ();
375             fillLookupTable ();
376         }
377     }
378
379     update();
380
381     return TRUE;
382 }
383
384 gboolean
385 PunctEditor::removeCharAfter (void)
386 {
387     if (G_UNLIKELY (m_cursor == m_text.length ()))
388         return FALSE;
389     m_selected_puncts.erase (m_selected_puncts.begin () + m_cursor);
390     m_text.erase (m_cursor, 1);
391     if (m_text.empty()) {
392         reset ();
393     }
394
395     update();
396
397     return TRUE;
398 }
399
400 void
401 PunctEditor::reset (void)
402 {
403     m_punct_mode = MODE_DISABLE;
404     m_selected_puncts.clear ();
405     m_punct_candidates.clear ();
406     fillLookupTable ();
407     Editor::reset ();
408 }
409
410 void
411 PunctEditor::candidateClicked (guint index, guint button, guint state)
412 {
413     selectCandidateInPage(index);
414 }
415
416 inline void
417 PunctEditor::commit (const gchar *str)
418 {
419     StaticText text(str);
420     commitText (text);
421     reset ();
422 }
423
424 void
425 PunctEditor::commit (void)
426 {
427     m_buffer.clear ();
428     for (std::vector<const gchar *>::iterator it = m_selected_puncts.begin ();
429          it != m_selected_puncts.end (); it++) {
430         m_buffer << *it;
431     }
432
433     commit (m_buffer);
434 }
435
436 inline gboolean
437 PunctEditor::selectCandidate (guint i)
438 {
439     switch (m_punct_mode) {
440     case MODE_INIT:
441         {
442             g_assert (m_cursor == 1);
443             m_lookup_table.setCursorPos (i);
444             m_selected_puncts[m_cursor - 1] = m_punct_candidates[i];
445             commit ();
446             return TRUE;
447         }
448     case MODE_NORMAL:
449         {
450             m_lookup_table.setCursorPos (i);
451             m_selected_puncts[m_cursor - 1] = m_punct_candidates[i];
452
453             /* if it is the last punct, commit the result */
454             if (m_cursor == m_text.length ()) {
455                 commit ();
456             }
457             else {
458                 moveCursorRight ();
459             }
460             return TRUE;
461         }
462     default:
463         g_assert_not_reached ();
464     }
465
466     return FALSE;
467 }
468
469 inline gboolean
470 PunctEditor::selectCandidateInPage (guint i)
471 {
472     guint page_size = m_lookup_table.pageSize ();
473     guint cursor_pos = m_lookup_table.cursorPos ();
474
475     if (G_UNLIKELY (i >= page_size))
476         return FALSE;
477
478     i += (cursor_pos / page_size) * page_size;
479
480     return selectCandidate (i);
481 }
482
483 void
484 PunctEditor::update (void)
485 {
486     updateLookupTable ();
487     updatePreeditText ();
488     updateAuxiliaryText ();
489 }
490
491 void
492 PunctEditor::fillLookupTable (void)
493 {
494     m_lookup_table.clear ();
495     m_lookup_table.setPageSize (m_config.pageSize ());
496     m_lookup_table.setOrientation (m_config.orientation ());
497
498     for (std::vector<const gchar *>::iterator it = m_punct_candidates.begin ();
499          it != m_punct_candidates.end (); it++) {
500         StaticText text (*it);
501         // text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x004466, 0, -1);
502         m_lookup_table.appendCandidate (text);
503     }
504 }
505
506 void
507 PunctEditor::updateLookupTable (void)
508 {
509     if (m_lookup_table.size ()) {
510         Editor::updateLookupTable (m_lookup_table, TRUE);
511     }
512     else {
513         hideLookupTable ();
514     }
515 }
516
517 static int
518 punct_cmp (const void *p1, const void *p2)
519 {
520     const gint s1 = (gint)(glong) p1;
521     const gchar *s2 = **(gchar ***) p2;
522     return s1 - s2[0];
523 }
524
525 void
526 PunctEditor::updatePunctCandidates (gchar ch)
527 {
528     const gchar *** brs;
529     const gchar ** res;
530
531     m_punct_candidates.clear();
532
533     brs = (const gchar ***) std::bsearch (reinterpret_cast<void *>(ch),
534                                           punct_table,
535                                           G_N_ELEMENTS (punct_table),
536                                           sizeof(punct_table[0]),
537                                           punct_cmp);
538     if (brs != NULL) {
539         for (res = (*brs) + 1; *res != NULL; ++res) {
540             m_punct_candidates.push_back (*res);
541         }
542     }
543     fillLookupTable ();
544 }
545
546 void
547 PunctEditor::updateAuxiliaryText (void)
548 {
549     switch (m_punct_mode) {
550     case MODE_DISABLE:
551         hideAuxiliaryText ();
552         break;
553     case MODE_INIT:
554         {
555             m_buffer = "`";
556             StaticText aux_text (m_buffer);
557             Editor::updateAuxiliaryText (aux_text, TRUE);
558         }
559         break;
560     case MODE_NORMAL:
561         {
562             if (m_cursor == 0) {
563                 hideAuxiliaryText ();
564             }
565             else {
566                 m_buffer.clear ();
567                 for (String::iterator i = m_text.begin (); i != m_text.end (); ++i) {
568                     if (i - m_text.begin () == (gint) m_cursor)
569                         m_buffer << '|';
570                     m_buffer << *i;
571                 }
572                 if (m_text.end () - m_text.begin () == (gint) m_cursor)
573                     m_buffer << '|';
574
575                 StaticText aux_text (m_buffer);
576                 Editor::updateAuxiliaryText (aux_text, TRUE);
577             }
578         }
579         break;
580     default:
581         g_assert_not_reached ();
582     }
583 }
584
585 void
586 PunctEditor::updatePreeditText (void)
587 {
588     switch (m_punct_mode) {
589     case MODE_DISABLE:
590         hidePreeditText ();
591         break;
592     case MODE_INIT:
593         {
594             m_buffer = m_punct_candidates[m_lookup_table.cursorPos ()];
595             StaticText preedit_text (m_buffer);
596             /* underline */
597             preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
598             Editor::updatePreeditText (preedit_text, m_cursor, TRUE);
599         }
600         break;
601     case MODE_NORMAL:
602         {
603             m_buffer.clear ();
604             for (std::vector<const gchar *>::iterator it = m_selected_puncts.begin ();
605                  it != m_selected_puncts.end (); it++) {
606                 m_buffer << *it;
607             }
608             StaticText preedit_text (m_buffer);
609             /* underline */
610             preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
611             Editor::updatePreeditText (preedit_text, m_cursor, TRUE);
612         }
613         break;
614     default:
615         g_assert_not_reached ();
616     }
617 }
618
619 };
620
621