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 
22 #include "PYPPinyinEditor.h"
23 #include "PYConfig.h"
24 #include "PYPinyinProperties.h"
25 #include "PYSimpTradConverter.h"
26 #include "PYHalfFullConverter.h"
27 #include "PYLibPinyin.h"
28 
29 using namespace PY;
30 
31 /* init static members*/
PinyinEditor(PinyinProperties & props,Config & config)32 PinyinEditor::PinyinEditor (PinyinProperties & props,
33                                               Config & config)
34     : PhoneticEditor (props, config)
35 {
36 }
37 
38 
39 /**
40  * process pinyin
41  */
42 inline gboolean
processPinyin(guint keyval,guint keycode,guint modifiers)43 PinyinEditor::processPinyin (guint keyval, guint keycode,
44                                       guint modifiers)
45 {
46     if (G_UNLIKELY (cmshm_filter (modifiers) != 0))
47         return m_text ? TRUE : FALSE;
48 
49     return insert (keyval);
50 }
51 
52 /**
53  * process numbers
54  */
55 inline gboolean
processNumber(guint keyval,guint keycode,guint modifiers)56 PinyinEditor::processNumber (guint keyval, guint keycode,
57                                       guint modifiers)
58 {
59     guint i;
60 
61     if (m_text.empty ())
62         return FALSE;
63 
64     switch (keyval) {
65     case IBUS_0:
66     case IBUS_KP_0:
67         i = 9;
68         break;
69     case IBUS_1 ... IBUS_9:
70         i = keyval - IBUS_1;
71         break;
72     case IBUS_KP_1 ... IBUS_KP_9:
73         i = keyval - IBUS_KP_1;
74         break;
75     default:
76         g_return_val_if_reached (FALSE);
77     }
78 
79     if (modifiers == 0)
80         selectCandidateInPage (i);
81 
82     update ();
83     return TRUE;
84 }
85 
86 inline gboolean
processPunct(guint keyval,guint keycode,guint modifiers)87 PinyinEditor::processPunct (guint keyval, guint keycode,
88                                      guint modifiers)
89 {
90     if (m_text.empty ())
91         return FALSE;
92 
93     if (cmshm_filter (modifiers) != 0)
94         return TRUE;
95 
96     switch (keyval) {
97     case IBUS_apostrophe:
98         return insert (keyval);
99     case IBUS_comma:
100         if (m_config.commaPeriodPage ()) {
101             pageUp ();
102             return TRUE;
103         }
104         break;
105     case IBUS_minus:
106         if (m_config.minusEqualPage ()) {
107             pageUp ();
108             return TRUE;
109         }
110         break;
111     case IBUS_period:
112         if (m_config.commaPeriodPage ()) {
113             pageDown ();
114             return TRUE;
115         }
116         break;
117     case IBUS_equal:
118         if (m_config.minusEqualPage ()) {
119             pageDown ();
120             return TRUE;
121         }
122         break;
123     }
124 
125     if (m_config.autoCommit ()) {
126         if (m_lookup_table.size ()) {
127             selectCandidate (m_lookup_table.cursorPos ());
128         }
129         commit ();
130         return FALSE;
131     }
132 
133     return TRUE;
134 }
135 
136 inline gboolean
processFunctionKey(guint keyval,guint keycode,guint modifiers)137 PinyinEditor::processFunctionKey (guint keyval, guint keycode,
138                                            guint modifiers)
139 {
140     if (m_text.empty ())
141         return FALSE;
142 
143     /* ignore numlock */
144     modifiers = cmshm_filter (modifiers);
145 
146     if (modifiers != 0 && modifiers != IBUS_CONTROL_MASK)
147         return TRUE;
148 
149     /* process some cursor control keys */
150     if (modifiers == 0) { /* no modifiers. */
151         switch (keyval) {
152         case IBUS_Shift_L:
153             if (!m_config.shiftSelectCandidate ())
154                 return FALSE;
155             selectCandidateInPage (1);
156             return TRUE;
157 
158         case IBUS_Shift_R:
159             if (!m_config.shiftSelectCandidate ())
160                 return FALSE;
161             selectCandidateInPage (2);
162             return TRUE;
163         }
164     }
165 
166     return PhoneticEditor::processFunctionKey (keyval, keycode,
167                                                         modifiers);
168 }
169 
170 gboolean
processKeyEvent(guint keyval,guint keycode,guint modifiers)171 PinyinEditor::processKeyEvent (guint keyval, guint keycode,
172                                         guint modifiers)
173 {
174     modifiers &= (IBUS_SHIFT_MASK |
175                   IBUS_CONTROL_MASK |
176                   IBUS_MOD1_MASK |
177                   IBUS_SUPER_MASK |
178                   IBUS_HYPER_MASK |
179                   IBUS_META_MASK |
180                   IBUS_LOCK_MASK);
181 
182     switch (keyval) {
183     /* letters */
184     case IBUS_a ... IBUS_z:
185         return processPinyin (keyval, keycode, modifiers);
186     case IBUS_0 ... IBUS_9:
187     case IBUS_KP_0 ... IBUS_KP_9:
188         return processNumber (keyval, keycode, modifiers);
189     case IBUS_exclam ... IBUS_slash:
190     case IBUS_colon ... IBUS_at:
191     case IBUS_bracketleft ... IBUS_quoteleft:
192     case IBUS_braceleft ... IBUS_asciitilde:
193         return processPunct (keyval, keycode, modifiers);
194     case IBUS_space:
195         return processSpace (keyval, keycode, modifiers);
196     default:
197         return processFunctionKey (keyval, keycode, modifiers);
198     }
199 }
200 
201 void
commit(const gchar * str)202 PinyinEditor::commit (const gchar *str)
203 {
204     if (G_UNLIKELY (m_text.empty ()))
205         return;
206 
207     m_buffer.clear ();
208 
209     /* sentence candidate */
210     m_buffer << str;
211 
212     /* text after pinyin */
213     const gchar *p = m_text.c_str() + m_pinyin_len;
214     if (G_UNLIKELY (m_props.modeFull ())) {
215         while (*p != '\0') {
216             m_buffer.appendUnichar (HalfFullConverter::toFull (*p++));
217         }
218     } else {
219         m_buffer << p;
220     }
221 
222     Text text (m_buffer.c_str ());
223     commitText (text);
224 
225     reset();
226 }
227 
228 void
updatePreeditText()229 PinyinEditor::updatePreeditText ()
230 {
231     guint num = 0;
232     pinyin_get_n_candidate (m_instance, &num);
233 
234     /* preedit text = guessed sentence + un-parsed pinyin text */
235     if (G_UNLIKELY (m_text.empty () || 0 == num)) {
236         hidePreeditText ();
237         return;
238     }
239 
240     m_buffer.clear ();
241 
242     /* probe nbest match candidate */
243     lookup_candidate_type_t type;
244     lookup_candidate_t * candidate = NULL;
245     pinyin_get_candidate (m_instance, 0, &candidate);
246     pinyin_get_candidate_type (m_instance, candidate, &type);
247 
248     gchar * sentence = NULL;
249     if (NBEST_MATCH_CANDIDATE == type) {
250         pinyin_get_sentence (m_instance, 0, &sentence);
251         m_buffer<<m_candidates[0].m_display_string;
252     }
253 
254     /* append rest text */
255     const gchar *p = m_text.c_str () + m_pinyin_len;
256     m_buffer << p;
257 
258     StaticText preedit_text (m_buffer);
259     /* underline */
260     preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
261 
262     size_t offset = 0;
263     guint cursor = getPinyinCursor ();
264     pinyin_get_character_offset(m_instance, sentence, cursor, &offset);
265     Editor::updatePreeditText (preedit_text, offset, TRUE);
266 }
267 
268 #if 0
269 void
270 PinyinEditor::updateAuxiliaryText ()
271 {
272     if (G_UNLIKELY (m_text.empty ())) {
273         hideAuxiliaryText ();
274         return;
275     }
276 
277     m_buffer.clear ();
278 
279     /* Note: cursor handling is defered to full/double pinyin editors. */
280 
281     guint len = 0;
282     pinyin_get_n_pinyin (m_instance, &len);
283 
284     for (guint i = 0; i < len; ++i) {
285         if (G_LIKELY (i))
286             m_buffer << ' ';
287 
288         PinyinKey *key = NULL;
289         pinyin_get_pinyin_key (m_instance, i, &key);
290 
291         gchar * str = NULL;
292         pinyin_get_pinyin_string (m_instance, key, &str);
293 
294         m_buffer << str;
295         g_free (str);
296     }
297 
298     /* append rest text */
299     const gchar *p = m_text.c_str() + m_pinyin_len;
300     m_buffer << p;
301 
302     StaticText aux_text (m_buffer);
303     Editor::updateAuxiliaryText (aux_text, TRUE);
304 }
305 #endif
306 
307 void
updateLookupTable()308 PinyinEditor::updateLookupTable ()
309 {
310     m_lookup_table.setPageSize (m_config.pageSize ());
311     m_lookup_table.setOrientation (m_config.orientation ());
312     PhoneticEditor::updateLookupTable ();
313 }
314 
315