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