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));