fixes compile
[platform/upstream/ibus-libpinyin.git] / src / PYPPinyinEngine.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) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
6  * Copyright (c) 2011 Peng Wu <alexepico@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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21  */
22 #include "PYPPinyinEngine.h"
23 #include <string>
24 #include "PYConfig.h"
25 #include "PYPConfig.h"
26 #include "PYPunctEditor.h"
27 #include "PYRawEditor.h"
28 #ifdef IBUS_BUILD_LUA_EXTENSION
29 #include "PYExtEditor.h"
30 #endif
31 #ifdef IBUS_BUILD_ENGLISH_INPUT_MODE
32 #include "PYEnglishEditor.h"
33 #endif
34 #ifdef IBUS_BUILD_STROKE_INPUT_MODE
35 #include "PYStrokeEditor.h"
36 #endif
37 #include "PYPFullPinyinEditor.h"
38 #include "PYPDoublePinyinEditor.h"
39 #include "PYFallbackEditor.h"
40
41 using namespace PY;
42
43 /* constructor */
44 LibPinyinPinyinEngine::LibPinyinPinyinEngine (IBusEngine *engine)
45     : Engine (engine),
46       m_props (LibPinyinPinyinConfig::instance ()),
47       m_prev_pressed_key (IBUS_VoidSymbol),
48       m_input_mode (MODE_INIT),
49       m_fallback_editor (new FallbackEditor (m_props, LibPinyinPinyinConfig::instance ()))
50 {
51     gint i;
52
53     m_double_pinyin = LibPinyinPinyinConfig::instance ().doublePinyin ();
54
55     if (m_double_pinyin)
56         m_editors[MODE_INIT].reset
57             (new LibPinyinDoublePinyinEditor (m_props, LibPinyinPinyinConfig::instance ()));
58     else
59         m_editors[MODE_INIT].reset
60             (new LibPinyinFullPinyinEditor (m_props, LibPinyinPinyinConfig::instance ()));
61
62     m_editors[MODE_PUNCT].reset
63         (new PunctEditor (m_props, LibPinyinPinyinConfig::instance ()));
64     m_editors[MODE_RAW].reset
65         (new RawEditor (m_props, LibPinyinPinyinConfig::instance ()));
66
67 #ifdef IBUS_BUILD_LUA_EXTENSION
68     m_editors[MODE_EXTENSION].reset (new ExtEditor (m_props, LibPinyinPinyinConfig::instance ()));
69 #else
70     m_editors[MODE_EXTENSION].reset (new Editor (m_props, LibPinyinPinyinConfig::instance ()));
71 #endif
72 #ifdef IBUS_BUILD_ENGLISH_INPUT_MODE
73     m_editors[MODE_ENGLISH].reset (new EnglishEditor (m_props, LibPinyinPinyinConfig::instance ()));
74 #else
75     m_editors[MODE_ENGLISH].reset (new Editor (m_props, LibPinyinPinyinConfig::instance ()));
76 #endif
77 #ifdef IBUS_BUILD_STROKE_INPUT_MODE
78     m_editors[MODE_STROKE].reset (new StrokeEditor (m_props, LibPinyinPinyinConfig::instance ()));
79 #else
80     m_editors[MODE_STROKE].reset (new Editor (m_props, LibPinyinPinyinConfig::instance ()));
81 #endif
82
83     m_props.signalUpdateProperty ().connect
84         (std::bind (&LibPinyinPinyinEngine::updateProperty, this, _1));
85
86     for (i = MODE_INIT; i < MODE_LAST; i++) {
87         connectEditorSignals (m_editors[i]);
88     }
89
90     connectEditorSignals (m_fallback_editor);
91 }
92
93 /* destructor */
94 LibPinyinPinyinEngine::~LibPinyinPinyinEngine (void)
95 {
96 }
97
98 gboolean
99 LibPinyinPinyinEngine::processKeyEvent (guint keyval, guint keycode, guint modifiers)
100 {
101     gboolean retval = FALSE;
102
103     /* check Shift or Ctrl + Release hotkey,
104      * and then ignore other Release key event */
105     if (modifiers & IBUS_RELEASE_MASK) {
106         /* press and release keyval are same,
107          * and no other key event between the press and release key event */
108         gboolean triggered = FALSE;
109
110         if (m_prev_pressed_key == keyval){
111             if (LibPinyinPinyinConfig::instance ().ctrlSwitch ()) {
112                 if (keyval == IBUS_Control_L || keyval == IBUS_Control_R)
113                     triggered = TRUE;
114             } else {
115                 if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
116                     triggered = TRUE;
117             }
118         }
119
120         if (triggered) {
121             if (!m_editors[MODE_INIT]->text ().empty ())
122                 m_editors[MODE_INIT]->reset ();
123             m_props.toggleModeChinese ();
124             return TRUE;
125         }
126
127         if (m_input_mode == MODE_INIT &&
128             m_editors[MODE_INIT]->text ().empty ()) {
129             /* If it is in init mode, and  no any previous input text,
130              * we will let client applications to handle release key event */
131             return FALSE;
132         }
133         else {
134             return TRUE;
135         }
136     }
137
138     /* Toggle simp/trad Chinese Mode when hotkey Ctrl + Shift + F pressed */
139     if (keyval == IBUS_F && scmshm_test (modifiers, (IBUS_SHIFT_MASK | IBUS_CONTROL_MASK))) {
140         m_props.toggleModeSimp ();
141         m_prev_pressed_key = IBUS_F;
142         return TRUE;
143     }
144
145     if (m_props.modeChinese ()) {
146         if (m_input_mode == MODE_INIT &&
147             (cmshm_filter (modifiers) == 0)) {
148             const String & text = m_editors[MODE_INIT]->text ();
149             if (text.empty ()) {
150                 switch (keyval) {
151                 case IBUS_grave:
152                     m_input_mode = MODE_PUNCT;
153                     break;
154 #ifdef IBUS_BUILD_LUA_EXTENSION
155                 case IBUS_i:
156                     // do not enable lua extension when use double pinyin.
157                     if (LibPinyinPinyinConfig::instance ().doublePinyin ())
158                         break;
159                     m_input_mode = MODE_EXTENSION;
160                     break;
161 #endif
162 #ifdef IBUS_BUILD_ENGLISH_INPUT_MODE
163                 case IBUS_v:
164                     // do not enable english mode when use double pinyin.
165                     if (LibPinyinPinyinConfig::instance ().doublePinyin ())
166                         break;
167                     m_input_mode = MODE_ENGLISH;
168                     break;
169 #endif
170 #ifdef IBUS_BUILD_STROKE_INPUT_MODE
171                 case IBUS_u:
172                     // do not enable stroke mode when use double pinyin.
173                     if (LibPinyinPinyinConfig::instance ().doublePinyin ())
174                         break;
175                     m_input_mode = MODE_STROKE;
176                     break;
177 #endif
178                 }
179             } else {
180                 /* TODO: Unknown */
181             }
182         }
183         retval = m_editors[m_input_mode]->processKeyEvent (keyval, keycode, modifiers);
184         if (G_UNLIKELY (retval &&
185                         m_input_mode != MODE_INIT &&
186                         m_editors[m_input_mode]->text ().empty ()))
187             m_input_mode = MODE_INIT;
188     }
189
190     if (G_UNLIKELY (!retval))
191         retval = m_fallback_editor->processKeyEvent (keyval, keycode, modifiers);
192
193     /* store ignored key event by editors */
194     m_prev_pressed_key = retval ? IBUS_VoidSymbol : keyval;
195
196     return retval;
197 }
198
199 void
200 LibPinyinPinyinEngine::focusIn (void)
201 {
202     /* TODO: check memory leak here,
203      *       or switch full/double pinyin when pinyin config is changed.*/
204     if (LibPinyinPinyinConfig::instance ().doublePinyin ()) {
205         if (!m_double_pinyin) {
206             m_editors[MODE_INIT].reset (new LibPinyinDoublePinyinEditor (m_props, LibPinyinPinyinConfig::instance ()));
207             connectEditorSignals (m_editors[MODE_INIT]);
208         }
209         m_double_pinyin = TRUE;
210     }
211     else {
212         if (m_double_pinyin) {
213             m_editors[MODE_INIT].reset (new LibPinyinFullPinyinEditor (m_props, LibPinyinPinyinConfig::instance ()));
214             connectEditorSignals (m_editors[MODE_INIT]);
215         }
216         m_double_pinyin = FALSE;
217     }
218
219     registerProperties (m_props.properties ());
220 }
221
222 void
223 LibPinyinPinyinEngine::focusOut (void)
224 {
225     reset ();
226 }
227
228 void
229 LibPinyinPinyinEngine::reset (void)
230 {
231     m_prev_pressed_key = IBUS_VoidSymbol;
232     m_input_mode = MODE_INIT;
233     for (gint i = 0; i < MODE_LAST; i++) {
234         m_editors[i]->reset ();
235     }
236     m_fallback_editor->reset ();
237 }
238
239 void
240 LibPinyinPinyinEngine::enable (void)
241 {
242     m_props.reset ();
243 }
244
245 void
246 LibPinyinPinyinEngine::disable (void)
247 {
248 }
249
250 void
251 LibPinyinPinyinEngine::pageUp (void)
252 {
253     m_editors[m_input_mode]->pageUp ();
254 }
255
256 void
257 LibPinyinPinyinEngine::pageDown (void)
258 {
259     m_editors[m_input_mode]->pageDown ();
260 }
261
262 void
263 LibPinyinPinyinEngine::cursorUp (void)
264 {
265     m_editors[m_input_mode]->cursorUp ();
266 }
267
268 void
269 LibPinyinPinyinEngine::cursorDown (void)
270 {
271     m_editors[m_input_mode]->cursorDown ();
272 }
273
274 inline void
275 LibPinyinPinyinEngine::showSetupDialog (void)
276 {
277     g_spawn_command_line_async
278         (LIBEXECDIR"/ibus-setup-libpinyin pinyin", NULL);
279 }
280
281 gboolean
282 LibPinyinPinyinEngine::propertyActivate (const char *prop_name, guint prop_state)
283 {
284     const static String setup ("setup");
285     if (m_props.propertyActivate (prop_name, prop_state)) {
286         return TRUE;
287     }
288     else if (setup == prop_name) {
289         showSetupDialog ();
290         return TRUE;
291     }
292     return FALSE;
293 }
294
295 void
296 LibPinyinPinyinEngine::candidateClicked (guint index, guint button, guint state)
297 {
298     m_editors[m_input_mode]->candidateClicked (index, button, state);
299 }
300
301 void
302 LibPinyinPinyinEngine::commitText (Text & text)
303 {
304     Engine::commitText (text);
305     if (m_input_mode != MODE_INIT)
306         m_input_mode = MODE_INIT;
307 #if 1
308     /* handle "<num>+.<num>+" here */
309     if (text.text ())
310         static_cast<FallbackEditor*> (m_fallback_editor.get ())->setPrevCommittedChar (*text.text ());
311     else
312         static_cast<FallbackEditor*> (m_fallback_editor.get ())->setPrevCommittedChar (0);
313 #endif
314 }
315
316 void
317 LibPinyinPinyinEngine::connectEditorSignals (EditorPtr editor)
318 {
319     editor->signalCommitText ().connect (
320         std::bind (&LibPinyinPinyinEngine::commitText, this, _1));
321
322     editor->signalUpdatePreeditText ().connect (
323         std::bind (&LibPinyinPinyinEngine::updatePreeditText, this, _1, _2, _3));
324     editor->signalShowPreeditText ().connect (
325         std::bind (&LibPinyinPinyinEngine::showPreeditText, this));
326     editor->signalHidePreeditText ().connect (
327         std::bind (&LibPinyinPinyinEngine::hidePreeditText, this));
328
329     editor->signalUpdateAuxiliaryText ().connect (
330         std::bind (&LibPinyinPinyinEngine::updateAuxiliaryText, this, _1, _2));
331     editor->signalShowAuxiliaryText ().connect (
332         std::bind (&LibPinyinPinyinEngine::showAuxiliaryText, this));
333     editor->signalHideAuxiliaryText ().connect (
334         std::bind (&LibPinyinPinyinEngine::hideAuxiliaryText, this));
335
336     editor->signalUpdateLookupTable ().connect (
337         std::bind (&LibPinyinPinyinEngine::updateLookupTable, this, _1, _2));
338     editor->signalUpdateLookupTableFast ().connect (
339         std::bind (&LibPinyinPinyinEngine::updateLookupTableFast, this, _1, _2));
340     editor->signalShowLookupTable ().connect (
341         std::bind (&LibPinyinPinyinEngine::showLookupTable, this));
342     editor->signalHideLookupTable ().connect (
343         std::bind (&LibPinyinPinyinEngine::hideLookupTable, this));
344 }