2 * Copyright (c) 2009 Kov Chai <tchaikov@gmail.com>
4 * The contents of this file are subject to the terms of either the GNU Lesser
5 * General Public License Version 2.1 only ("LGPL") or the Common Development and
6 * Distribution License ("CDDL")(collectively, the "License"). You may not use this
7 * file except in compliance with the License. You can obtain a copy of the CDDL at
8 * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
9 * http://www.opensource.org/licenses/lgpl-license.php. See the License for the
10 * specific language governing permissions and limitations under the License. When
11 * distributing the software, include this License Header Notice in each file and
12 * include the full text of the License in the License file as well as the
15 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
17 * For Covered Software in this distribution, this License shall be governed by the
18 * laws of the State of California (excluding conflict-of-law provisions).
19 * Any litigation relating to this License shall be subject to the jurisdiction of
20 * the Federal Courts of the Northern District of California and the state courts
21 * of the State of California, with venue lying in Santa Clara County, California.
25 * If you wish your version of this file to be governed by only the CDDL or only
26 * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
27 * include this software in this distribution under the [CDDL or LGPL Version 2.1]
28 * license." If you don't indicate a single choice of license, a recipient has the
29 * option to distribute your version of this file under either the CDDL or the LGPL
30 * Version 2.1, or to extend the choice of license to its licensees as provided
31 * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
32 * Version 2 license, then the option applies only if the new code is made subject
33 * to such option by the copyright holder.
40 #include <sunpinyin.h>
42 #include "sunpinyin_property.h"
43 #include "sunpinyin_lookup_table.h"
44 #include "sunpinyin_config.h"
45 #include "sunpinyin_config_keys.h"
46 #include "imi_ibus_win.h"
47 #include "ibus_portable.h"
48 #include "sunpinyin_engine.h"
50 extern ibus::Config config;
52 SunPinyinEngine::SunPinyinEngine(IBusEngine *engine)
54 m_status_prop(SunPinyinProperty::create_status_prop(engine)),
55 m_letter_prop(SunPinyinProperty::create_letter_prop(engine)),
56 m_punct_prop(SunPinyinProperty::create_punct_prop(engine)),
59 m_hotkey_profile(NULL)
61 CSunpinyinSessionFactory& factory = CSunpinyinSessionFactory::getFactory();
63 CSunpinyinSessionFactory::EPyScheme pinyin_scheme =
64 m_config.get_py_scheme(CSunpinyinSessionFactory::QUANPIN);
65 factory.setPinyinScheme(pinyin_scheme);
66 if (pinyin_scheme == CSunpinyinSessionFactory::QUANPIN) {
67 update_fuzzy_pinyins();
68 update_correction_pinyins();
71 update_shuangpin_type();
73 update_user_data_dir();
74 update_punct_mappings();
76 factory.setCandiWindowSize(m_config.get(CONFIG_GENERAL_PAGE_SIZE, 10));
78 m_pv = factory.createSession();
82 m_hotkey_profile = new CHotkeyProfile();
83 m_pv->setHotkeyProfile(m_hotkey_profile);
85 m_wh = new CIBusWinHandler(this);
86 m_pv->attachWinHandler(m_wh);
88 m_prop_list = ibus_prop_list_new();
90 ibus_prop_list_append(m_prop_list, m_status_prop);
91 ibus_prop_list_append(m_prop_list, m_letter_prop);
92 ibus_prop_list_append(m_prop_list, m_punct_prop);
93 ibus_prop_list_append(m_prop_list, m_setup_prop);
98 SunPinyinEngine::~SunPinyinEngine()
101 CSunpinyinSessionFactory& factory =
102 CSunpinyinSessionFactory::getFactory();
103 factory.destroySession(m_pv);
107 delete m_hotkey_profile;
111 translate_key(guint key_val, guint /*key_code*/, guint modifiers)
113 // XXX: may need to move this logic into CKeyEvent
114 if (key_val > 0x20 && key_val < 0x7f // isprint(key_val) && !isspace(key_val)
115 && !(modifiers & IM_CTRL_MASK)) {
116 // we only care about key_val here
117 return CKeyEvent(key_val, key_val, modifiers);
119 // what matters is key_code, but ibus sents me key_code as key_val
120 return CKeyEvent(key_val, 0, modifiers);
125 SunPinyinEngine::process_key_event (guint key_val,
129 CKeyEvent key = translate_key(key_val, key_code, modifiers);
131 if (!m_pv->getStatusAttrValue(CIBusWinHandler::STATUS_ID_CN)) {
132 // we are in English input mode
133 if (!m_hotkey_profile->isModeSwitchKey(key)) {
134 m_hotkey_profile->rememberLastKey(key);
137 } else if (m_hotkey_profile->isModeSwitchKey(key)) {
138 m_pv->onKeyEvent(CKeyEvent(IM_VK_ENTER, 0, 0));
139 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN, false);
143 return m_pv->onKeyEvent(key);
147 SunPinyinEngine::focus_in ()
149 ibus_engine_register_properties(m_engine, m_prop_list);
150 m_pv->updateWindows(CIMIView::PREEDIT_MASK | CIMIView::CANDIDATE_MASK);
154 SunPinyinEngine::focus_out ()
160 SunPinyinEngine::reset ()
162 m_pv->updateWindows(m_pv->clearIC());
166 SunPinyinEngine::enable ()
168 bool is_cn = m_config.is_initial_mode_cn();
169 m_status_prop.update(is_cn);
170 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN, is_cn);
172 bool is_letter_full = m_config.is_initial_letter_full();
173 m_letter_prop.update(is_letter_full);
174 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL, is_letter_full);
176 bool is_punct_full = m_config.is_initial_punct_full();
177 m_punct_prop.update(is_punct_full);
178 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, is_punct_full);
182 SunPinyinEngine::disable ()
187 SunPinyinEngine::page_up ()
189 m_pv->onCandidatePageRequest(-1, true /* relative */);
193 SunPinyinEngine::page_down ()
195 m_pv->onCandidatePageRequest(1, true /* relative */);
199 SunPinyinEngine::property_activate (const std::string& property, unsigned /*state*/)
201 if (m_status_prop.toggle(property)) {
202 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN,
203 m_status_prop.state());
204 } else if (m_letter_prop.toggle(property)) {
205 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL,
206 m_letter_prop.state());
207 } else if (m_punct_prop.toggle(property)) {
208 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC,
209 m_punct_prop.state());
211 // try to launch the setup UI
212 m_setup_prop.launch(property);
217 SunPinyinEngine::candidate_clicked (guint index)
219 m_pv->onCandidateSelectRequest(index);
223 SunPinyinEngine::cursor_up ()
225 if (m_lookup_table.cursor_up()) {
226 update_lookup_table();
231 SunPinyinEngine::cursor_down ()
233 if (m_lookup_table.cursor_down()) {
234 update_lookup_table();
239 SunPinyinEngine::onConfigChanged(const COptionEvent& event)
241 if (event.name == CONFIG_GENERAL_MEMORY_POWER) {
242 update_history_power();
243 } else if (event.name == CONFIG_GENERAL_PAGE_SIZE) {
244 update_cand_window_size();
245 } else if (event.name == CONFIG_GENERAL_CHARSET_LEVEL) {
246 update_charset_level();
247 } else if (event.name == CONFIG_GENERAL_MAX_BEST) {
249 } else if (event.name == CONFIG_GENERAL_MAX_TAIL_CANDIDATE) {
250 update_max_tail_candidate();
251 } else if (event.name == CONFIG_KEYBOARD_MODE_SWITCH) {
253 } else if (event.name == CONFIG_KEYBOARD_PUNCT_SWITCH) {
255 } else if (event.name == CONFIG_KEYBOARD_PAGE_COMMA) {
256 update_page_key_comma();
257 } else if (event.name == CONFIG_KEYBOARD_PAGE_MINUS) {
258 update_page_key_minus();
259 } else if (event.name == CONFIG_KEYBOARD_PAGE_BRACKET) {
260 update_page_key_bracket();
261 } else if (event.name == CONFIG_QUANPIN_FUZZYSEGS_ENABLED) {
263 } else if (event.name == CONFIG_KEYBOARD_CANCEL_BACKSPACE) {
264 update_cancel_with_backspace();
265 } else if (event.name == CONFIG_KEYBOARD_SMARK_PUNCT) {
273 SunPinyinEngine::update_config()
275 update_history_power();
276 update_cand_window_size();
278 update_max_tail_candidate();
279 update_charset_level();
280 update_page_key_minus();
281 update_page_key_comma();
282 update_page_key_bracket();
285 update_cancel_with_backspace();
287 update_punct_mappings();
288 update_candi_delete_key();
289 // update_quanpin_config();
290 // update_shuangpin_config();
294 SunPinyinEngine::commit_string (const std::wstring& str)
297 text = ibus_text_new_from_ucs4((const gunichar*) str.c_str());
298 ibus_engine_commit_text(m_engine, text);
302 SunPinyinEngine::update_candidates(const ICandidateList& cl)
304 if (m_lookup_table.update_candidates(cl) > 0)
305 update_lookup_table();
307 ibus_engine_hide_lookup_table (m_engine);
311 SunPinyinEngine::update_lookup_table()
313 ibus_engine_update_lookup_table(m_engine, m_lookup_table, TRUE);
317 SunPinyinEngine::is_valid() const
323 find_embed_preedit_pos(const IPreeditString& preedit)
325 int mask = IPreeditString::USER_CHOICE & IPreeditString::HANZI_CHAR;
326 for (size_t i = 0; i < preedit.charTypeSize(); i++) {
327 if ((preedit.charTypeAt(i) & mask) == 0) {
331 return preedit.charTypeSize();
334 enum {ORANGE = 0xE76F00, GRAY_BLUE = 0x35556B, WHITE = 0xFFFFFF,
338 SunPinyinEngine::update_preedit_string(const IPreeditString& preedit)
340 if (preedit.size() > 0) {
341 wstring wstr(preedit.string());
342 int embed_pos = find_embed_preedit_pos(preedit);
343 wstring embed_wstr = wstr.substr(0, embed_pos);
344 wstring preedit_wstr = wstr.substr(embed_pos);
345 const gunichar* embed_cstr = (const gunichar*) embed_wstr.c_str();
346 const gunichar* preedit_cstr = (const gunichar*) preedit_wstr.c_str();
347 IBusText* preedit_text = ibus_text_new_from_ucs4(preedit_cstr);
348 int caret = preedit.caret() - embed_pos;
350 if (preedit.caret() < preedit.size()) {
351 ibus_text_append_attribute(preedit_text, IBUS_ATTR_TYPE_FOREGROUND,
352 WHITE, caret, preedit_wstr.size());
353 ibus_text_append_attribute(preedit_text, IBUS_ATTR_TYPE_BACKGROUND,
354 GRAY_BLUE, caret, preedit_wstr.size());
356 ibus_engine_update_auxiliary_text(m_engine, preedit_text, TRUE);
358 ibus_engine_update_preedit_text(m_engine,
359 ibus_text_new_from_ucs4(embed_cstr),
360 preedit.caret(), TRUE);
363 ibus_engine_hide_auxiliary_text(m_engine);
364 ibus_engine_hide_preedit_text(m_engine);
369 SunPinyinEngine::update_status_property(bool cn)
371 m_status_prop.update(cn);
375 SunPinyinEngine::update_punct_property(bool full)
377 m_punct_prop.update(full);
381 SunPinyinEngine::update_letter_property(bool full)
383 m_letter_prop.update(full);
387 SunPinyinEngine::update_history_power()
389 unsigned power = m_config.get(CONFIG_GENERAL_MEMORY_POWER, 3);
390 CIMIContext* ic = m_pv->getIC();
392 ic->setHistoryPower(power);
396 SunPinyinEngine::update_charset_level()
398 unsigned charset = m_config.get(CONFIG_GENERAL_CHARSET_LEVEL, GBK);
399 CIMIContext* ic = m_pv->getIC();
401 charset &= 3; // charset can only be 0,1,2 or 3
402 ic->setCharsetLevel(charset);
406 SunPinyinEngine::update_cand_window_size()
408 unsigned size = m_config.get(CONFIG_GENERAL_PAGE_SIZE, 10);
409 m_pv->setCandiWindowSize(size);
413 SunPinyinEngine::update_mode_key()
415 std::string mode_switch("Shift");
416 mode_switch = m_config.get(CONFIG_KEYBOARD_MODE_SWITCH, mode_switch);
418 CKeyEvent shift_l (IM_VK_SHIFT_L, 0, IM_SHIFT_MASK|IM_RELEASE_MASK);
419 CKeyEvent shift_r (IM_VK_SHIFT_R, 0, IM_SHIFT_MASK|IM_RELEASE_MASK);
420 CKeyEvent control_l(IM_VK_CONTROL_L, 0, IM_CTRL_MASK|IM_RELEASE_MASK);
421 CKeyEvent control_r(IM_VK_CONTROL_R, 0, IM_CTRL_MASK|IM_RELEASE_MASK);
423 if (mode_switch == "Shift") {
424 m_hotkey_profile->removeModeSwitchKey(control_l);
425 m_hotkey_profile->removeModeSwitchKey(control_r);
426 m_hotkey_profile->addModeSwitchKey(shift_l);
427 m_hotkey_profile->addModeSwitchKey(shift_r);
428 } else if (mode_switch == "Control") {
429 m_hotkey_profile->removeModeSwitchKey(shift_l);
430 m_hotkey_profile->removeModeSwitchKey(shift_r);
431 m_hotkey_profile->addModeSwitchKey(control_l);
432 m_hotkey_profile->addModeSwitchKey(control_r);
437 SunPinyinEngine::update_punct_key()
439 std::string punct_switch("ControlComma");
440 punct_switch = m_config.get(CONFIG_KEYBOARD_PUNCT_SWITCH, punct_switch);
441 if (punct_switch == "ControlComma") {
442 m_hotkey_profile->setPunctSwitchKey(CKeyEvent(IM_VK_COMMA, 0, IM_CTRL_MASK));
443 } else if (punct_switch == "ControlPeriod") {
444 m_hotkey_profile->setPunctSwitchKey(CKeyEvent(IM_VK_PERIOD, 0, IM_CTRL_MASK));
449 SunPinyinEngine::update_page_key_minus()
451 update_page_key(CONFIG_KEYBOARD_PAGE_MINUS, false,
452 IM_VK_MINUS, IM_VK_EQUALS);
456 SunPinyinEngine::update_page_key_comma()
458 update_page_key(CONFIG_KEYBOARD_PAGE_COMMA, false,
459 IM_VK_COMMA, IM_VK_PERIOD);
463 SunPinyinEngine::update_page_key_bracket()
465 update_page_key(CONFIG_KEYBOARD_PAGE_BRACKET, false,
466 IM_VK_OPEN_BRACKET, IM_VK_CLOSE_BRACKET);
470 SunPinyinEngine::update_page_key(const char* conf_key, bool default_val,
471 unsigned page_up, unsigned page_down)
473 bool enabled = m_config.get(conf_key, default_val);
476 m_hotkey_profile->addPageUpKey(CKeyEvent(page_up, 0));
477 m_hotkey_profile->addPageDownKey(CKeyEvent(page_down, 0));
479 m_hotkey_profile->removePageUpKey(CKeyEvent(page_up, 0));
480 m_hotkey_profile->removePageDownKey(CKeyEvent(page_down, 0));
485 SunPinyinEngine::update_cancel_with_backspace()
487 bool enabled = m_config.get(CONFIG_KEYBOARD_CANCEL_BACKSPACE, true);
488 m_pv->setCancelOnBackspace(enabled);
492 SunPinyinEngine::update_smart_punc()
494 m_pv->setSmartPunct(m_config.get(CONFIG_KEYBOARD_SMARK_PUNCT, true));
498 SunPinyinEngine::update_max_best()
500 if (m_pv->getIC() == NULL) {
503 int oldval = (int) m_pv->getIC()->getMaxBest();
504 m_pv->getIC()->setMaxBest(m_config.get(CONFIG_GENERAL_MAX_BEST, oldval));
508 SunPinyinEngine::update_max_tail_candidate()
510 if (m_pv->getIC() == NULL) {
513 int oldval = (int) m_pv->getIC()->getMaxTailCandidateNum();
514 m_pv->getIC()->setMaxTailCandidateNum(
515 m_config.get(CONFIG_GENERAL_MAX_TAIL_CANDIDATE, oldval));
518 string_pairs parse_pairs(const std::vector<std::string>& strings)
521 for (std::vector<std::string>::const_iterator pair = strings.begin();
522 pair != strings.end(); ++pair) {
524 std::string::size_type found = pair->find(':');
525 if (found == pair->npos || pair->length() < 3)
527 if (found == 0 && (*pair)[0] == ':')
530 pairs.push_back(make_pair(pair->substr(0, found),
531 pair->substr(found+1)));
536 // the mappings in default_pairs will override the ones in user_pairs
537 string_pairs merge_pairs(const string_pairs& default_pairs,
538 const string_pairs& user_pairs)
540 typedef std::map<std::string, int> Indexes;
543 for (string_pairs::const_iterator it = default_pairs.begin();
544 it != default_pairs.end(); ++it, ++index) {
545 Indexes::iterator found = indexes.find(it->first);
546 if (found == indexes.end()) {
547 indexes[it->first] = index;
549 // it is a paired punct.
550 indexes[it->first] = -found->second;
553 string_pairs result(default_pairs);
554 for (string_pairs::const_iterator it = user_pairs.begin();
555 it != user_pairs.end(); ++it) {
556 Indexes::iterator found = indexes.find(it->first);
557 if (found == indexes.end()) {
558 result.push_back(*it);
559 } else if (found->second >= 0) {
560 result[found->second] = *it;
562 // it is a paired punct,
563 // but we don't support this kind of mapping yet,
564 // so quietly ignore it.
571 SunPinyinEngine::update_punct_mappings()
573 CSimplifiedChinesePolicy& policy = ASimplifiedChinesePolicy::instance();
574 if (m_config.get(PINYIN_PUNCTMAPPING_ENABLED, false)) {
575 std::vector<std::string> mappings;
576 mappings = m_config.get(PINYIN_PUNCTMAPPING_MAPPINGS, mappings);
577 string_pairs pairs(merge_pairs(policy.getDefaultPunctMapping(),
578 parse_pairs(mappings)));
579 policy.setPunctMapping(pairs);
584 SunPinyinEngine::update_user_data_dir()
586 std::stringstream user_data_dir;
587 user_data_dir << g_get_home_dir()
588 << G_DIR_SEPARATOR_S << ".sunpinyin";
589 ASimplifiedChinesePolicy::instance().setUserDataDir(user_data_dir.str());
593 SunPinyinEngine::update_fuzzy_pinyins()
595 bool enabled = m_config.get(QUANPIN_FUZZY_ENABLED, false);
596 AQuanpinSchemePolicy::instance().setFuzzyForwarding(enabled);
597 AShuangpinSchemePolicy::instance().setFuzzyForwarding(enabled);
600 std::vector<std::string> fuzzy_pinyins;
601 fuzzy_pinyins = m_config.get(QUANPIN_FUZZY_PINYINS, fuzzy_pinyins);
602 AQuanpinSchemePolicy::instance().setFuzzyPinyinPairs(parse_pairs(fuzzy_pinyins));
603 AShuangpinSchemePolicy::instance().setFuzzyPinyinPairs(parse_pairs(fuzzy_pinyins));
607 SunPinyinEngine::update_correction_pinyins()
609 bool enabled = m_config.get(QUANPIN_AUTOCORRECTION_ENABLED, false);
610 AQuanpinSchemePolicy::instance().setAutoCorrecting(enabled);
613 std::vector<std::string> correction_pinyins;
614 correction_pinyins = m_config.get(QUANPIN_AUTOCORRECTION_PINYINS, correction_pinyins);
615 AQuanpinSchemePolicy::instance().setAutoCorrectionPairs(parse_pairs(correction_pinyins));
619 SunPinyinEngine::update_fuzzy_segs()
621 bool enable_fuzzy_segs = m_config.get(CONFIG_QUANPIN_FUZZYSEGS_ENABLED, false);
622 AQuanpinSchemePolicy::instance().setFuzzySegmentation(enable_fuzzy_segs);
623 bool enable_inner_fuzzy = m_config.get(CONFIG_QUANPIN_INNERFUZZY_ENABLED, false);
624 AQuanpinSchemePolicy::instance().setInnerFuzzySegmentation(CONFIG_QUANPIN_INNERFUZZY_ENABLED);
628 SunPinyinEngine::update_shuangpin_type()
630 EShuangpinType shuangpin_type = MS2003;
631 shuangpin_type = (EShuangpinType) m_config.get(SHUANGPIN_TYPE, (int) shuangpin_type);
632 AShuangpinSchemePolicy::instance().setShuangpinType(shuangpin_type);
636 SunPinyinEngine::update_candi_delete_key()
638 /* FIXME: need to get candi_delete_key from user's configuration */
639 m_hotkey_profile->setCandiDeleteKey(CKeyEvent(0, 0, IM_ALT_MASK));