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