2 * Copyright (c) 2007 Kov Chai <tchaikov@gmail.com>
3 * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
5 * The contents of this file are subject to the terms of either the GNU Lesser
6 * General Public License Version 2.1 only ("LGPL") or the Common Development and
7 * Distribution License ("CDDL")(collectively, the "License"). You may not use this
8 * file except in compliance with the License. You can obtain a copy of the CDDL at
9 * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
10 * http://www.opensource.org/licenses/lgpl-license.php. See the License for the
11 * specific language governing permissions and limitations under the License. When
12 * distributing the software, include this License Header Notice in each file and
13 * include the full text of the License in the License file as well as the
16 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
18 * For Covered Software in this distribution, this License shall be governed by the
19 * laws of the State of California (excluding conflict-of-law provisions).
20 * Any litigation relating to this License shall be subject to the jurisdiction of
21 * the Federal Courts of the Northern District of California and the state courts
22 * of the State of California, with venue lying in Santa Clara County, California.
26 * If you wish your version of this file to be governed by only the CDDL or only
27 * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
28 * include this software in this distribution under the [CDDL or LGPL Version 2.1]
29 * license." If you don't indicate a single choice of license, a recipient has the
30 * option to distribute your version of this file under either the CDDL or the LGPL
31 * Version 2.1, or to extend the choice of license to its licensees as provided
32 * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
33 * Version 2 license, then the option applies only if the new code is made subject
34 * to such option by the copyright holder.
36 * Modifications by Samsung Electronics Co., Ltd.
38 * 1.Added always candidate show feature
39 * 2.Added auto commit feature for mobile user
42 #define Uses_STL_AUTOPTR
43 #define Uses_STL_FUNCTIONAL
44 #define Uses_STL_VECTOR
45 #define Uses_STL_IOSTREAM
46 #define Uses_STL_FSTREAM
47 #define Uses_STL_ALGORITHM
49 #define Uses_STL_UTILITY
50 #define Uses_STL_IOMANIP
52 #define Uses_SCIM_UTILITY
53 #define Uses_SCIM_IMENGINE
54 #define Uses_SCIM_ICONV
55 #define Uses_SCIM_CONFIG_BASE
56 #define Uses_SCIM_CONFIG_PATH
57 #define Uses_SCIM_LOOKUP_TABLE
58 #define Uses_SCIM_DEBUG
60 #include <sys/types.h>
64 #include <Ecore_IMF.h>
66 #include <imi_options.h>
68 #include <ic_history.h>
72 #include "imi_scimwin.h"
73 #include "sunpinyin_utils.h"
74 #include "sunpinyin_keycode.h"
75 #include "sunpinyin_lookup_table.h"
76 #include "sunpinyin_imengine.h"
77 #include "sunpinyin_imengine_config_keys.h"
78 #include "sunpinyin_private.h"
80 #define SCIM_PROP_STATUS "/IMEngine/SunPinyin/Status"
81 #define SCIM_PROP_LETTER "/IMEngine/SunPinyin/Letter"
82 #define SCIM_PROP_PUNCT "/IMEngine/SunPinyin/Punct"
84 #ifndef SCIM_SUNPINYIN_DATADIR
85 #define SCIM_SUNPINYIN_DATADIR "/usr/share/scim/sunpinyin"
89 #define SCIM_ICONDIR "/usr/share/scim/icons"
92 #ifndef SCIM_SUNPINYIN_ICON_FILE
93 #define SCIM_SUNPINYIN_ICON_FILE (SCIM_ICONDIR "/sunpinyin_logo.png")
96 #define SCIM_FULL_LETTER_ICON (SCIM_ICONDIR "/full-letter.png")
97 #define SCIM_HALF_LETTER_ICON (SCIM_ICONDIR "/half-letter.png")
98 #define SCIM_FULL_PUNCT_ICON (SCIM_ICONDIR "/full-punct.png")
99 #define SCIM_HALF_PUNCT_ICON (SCIM_ICONDIR "/half-punct.png")
101 using namespace scim;
103 static IMEngineFactoryPointer _scim_pinyin_factory (0);
105 static ConfigPointer _scim_config (0);
107 static Property _status_property (SCIM_PROP_STATUS, "");
108 static Property _letter_property (SCIM_PROP_LETTER, "");
109 static Property _punct_property (SCIM_PROP_PUNCT, "");
111 static char g_common_symbol[]={'#','$','%','^','&','*','@'};
114 void scim_module_init (void)
116 SCIM_DEBUG_IMENGINE (3) << "scim_module_init\n";
117 bindtextdomain (GETTEXT_PACKAGE, SCIM_SUNPINYIN_LOCALEDIR);
118 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
121 void scim_module_exit (void)
123 _scim_pinyin_factory.reset ();
124 _scim_config.reset ();
127 uint32 scim_imengine_module_init (const ConfigPointer &config)
129 SCIM_DEBUG_IMENGINE (3) << "module_init\n";
130 _status_property.set_tip (_("The status of the current input method. Click to change it."));
131 _status_property.set_label ("英");
133 _letter_property.set_icon (SCIM_HALF_LETTER_ICON);
134 _letter_property.set_tip (_("The input mode of the letters. Click to toggle between half and full."));
135 _letter_property.set_label (_("Full/Half Letter"));
137 _punct_property.set_icon (SCIM_HALF_PUNCT_ICON);
138 _punct_property.set_tip (_("The input mode of the puncutations. Click to toggle between half and full."));
139 _punct_property.set_label (_("Full/Half Punct"));
141 _scim_config = config;
145 IMEngineFactoryPointer scim_imengine_module_create_factory (uint32 engine)
147 SCIM_DEBUG_IMENGINE (3) << "entering scim_imengine_module_create_factory()\n";
148 if (engine != 0) return IMEngineFactoryPointer (0);
149 if (_scim_pinyin_factory.null ()) {
150 SunPyFactory *factory = new SunPyFactory (_scim_config);
151 if (factory->valid ())
152 _scim_pinyin_factory = factory;
156 return _scim_pinyin_factory;
160 // implementation of SunPyFactory
161 SunPyFactory::SunPyFactory (const ConfigPointer &config)
165 SCIM_DEBUG_IMENGINE (3) << "SunPyFactory()\n";
166 set_languages ("zh_CN");
167 m_name = utf8_mbstowcs ("SunPinyin");
169 m_reload_signal_connection = m_config->signal_connect_reload (slot (this, &SunPyFactory::reload_config));
170 m_hotkey_profile = new CHotkeyProfile();
174 SunPyFactory::init ()
179 valid = load_user_config();
182 // postpone the load_user_data() to the ctor of SunPyInstance
187 SunPyFactory::load_user_config()
189 // Load configurations.
194 SunPyFactory::~SunPyFactory ()
196 SCIM_DEBUG_IMENGINE (3) << "~SunPyFactory()\n";
197 m_reload_signal_connection.disconnect ();
198 delete m_hotkey_profile;
202 SunPyFactory::get_name () const
208 SunPyFactory::get_authors () const
210 return utf8_mbstowcs (
211 String (_("Lei Zhang, <Phill.Zhang@sun.com>; Shuguagn Yan, <Ervin.Yan@sun.com>")));
215 SunPyFactory::get_credits () const
217 return utf8_mbstowcs (
218 String (_("Ported by Kov Chai, <tchaikov@gmail.com>")));
222 SunPyFactory::get_help () const
225 String (_("Hot Keys:"
227 " Switch between English/Chinese mode."
228 "\n\n Control+period:\n"
229 " Switch between full/half width punctuation mode."
230 "\n\n Shift+space:\n"
231 " Switch between full/half width letter mode."
233 " Page up in lookup table."
235 " Page down in lookup table."
237 " Cancel current syllable.\n"));
238 return utf8_mbstowcs (help);
242 SunPyFactory::get_uuid () const
244 return String ("3240fe82-585a-4f4a-96b3-0cad779c3b51");
248 SunPyFactory::get_icon_file () const
250 return String (SCIM_SUNPINYIN_ICON_FILE);
253 IMEngineInstancePointer
254 SunPyFactory::create_instance (const String& encoding, int id)
256 SCIM_DEBUG_IMENGINE (3) << "SunPyFactory::create_instance(" << id << ")\n";
257 return new SunPyInstance (this, m_hotkey_profile, encoding, id);
261 SunPyFactory::reload_config (const ConfigPointer &config)
267 // implementation of SunPyInstance
268 SunPyInstance::SunPyInstance (SunPyFactory *factory,
269 CHotkeyProfile *hotkey_profile,
270 const String& encoding,
272 : IMEngineInstanceBase (factory, encoding, id),
276 m_hotkey_profile (hotkey_profile),
278 m_common_lookup_table(0),
280 m_lookup_table_always_on (false)
282 SCIM_DEBUG_IMENGINE (3) << get_id() << ": SunPyInstance()\n";
283 create_session(hotkey_profile);
285 m_reload_signal_connection = factory->m_config->signal_connect_reload (slot (this, &SunPyInstance::reload_config));
286 init_lookup_table_labels ();
289 SunPyInstance::~SunPyInstance ()
291 SCIM_DEBUG_IMENGINE (3) << get_id() << ": ~SunPyInstance()\n";
292 m_reload_signal_connection.disconnect ();
297 translate_key(const KeyEvent& key)
299 // XXX: may need to move this logic into CKeyEvent
300 if (!(key.code & 0xff00) && isprint(key.code) && !isspace(key.code) && !(key.mask & IM_CTRL_MASK)) {
301 // we only care about key_val here
302 return CKeyEvent(0, key.code, key.mask);
304 // what matters is key_code, but ibus sents me key_code as key_val
305 return CKeyEvent(key.code, 0, key.mask);
310 SunPyInstance::process_key_event (const KeyEvent& key)
312 SCIM_DEBUG_IMENGINE (3) << get_id() << ": process_key_event(" << m_focused << ", " <<
317 if (!m_focused) return false;
319 CKeyEvent ev = translate_key(key);
321 if ( !m_pv->getStatusAttrValue(CScimWinHandler::STATUS_ID_CN) ) {
322 // we are in English input mode
323 if ( !m_hotkey_profile->isModeSwitchKey(ev) ) {
324 m_hotkey_profile->rememberLastKey(ev);
328 return ( key.is_key_release() ||
329 m_pv->onKeyEvent(ev) );
333 SunPyInstance::select_candidate (unsigned int item)
335 if(m_lookup_table->number_of_candidates() == 0 && item < sizeof(g_common_symbol)/sizeof(char)) {
336 char _str[2]={g_common_symbol[item],0};
337 commit_string(utf8_mbstowcs(_str));
340 m_pv->onCandidateSelectRequest(item);
341 // m_pv->makeSelection(item);
345 SunPyInstance::update_lookup_table_page_size (unsigned int page_size)
348 SCIM_DEBUG_IMENGINE (3) << ": update_lookup_table_page_size(" << page_size << ")\n";
349 m_pv->setCandiWindowSize(page_size);
350 m_lookup_table->set_page_size(page_size);
355 SunPyInstance::lookup_table_page_up ()
358 m_pv->onCandidatePageRequest(-1, true);
362 SunPyInstance::lookup_page_up()
364 m_lookup_table->page_up();
365 // m_lookup_table->set_page_size(m_pv->s_CandiWindowSize);
369 SunPyInstance::lookup_page_down()
371 m_lookup_table->page_down();
372 // m_lookup_table->set_page_size(m_pv->s_CandiWindowSize);
376 SunPyInstance::lookup_table_page_down ()
378 // XXX, it would be great, if View class expose a page_up() method
379 // m_pv->onKeyEvent(IM_VK_PAGE_DOWN, 0, 0);
380 // classic View overrides this method
381 // but modern View uses the default dummy implementation
383 m_pv->onCandidatePageRequest(1, true);
387 SunPyInstance::move_preedit_caret (unsigned int /*pos*/)
392 SunPyInstance::reset ()
394 SCIM_DEBUG_IMENGINE (3) << get_id() << ": reset()\n";
396 m_lookup_table->clear ();
397 hide_preedit_string ();
398 //hide_aux_string ();
399 //m_pv->updateWindows(m_pv->clearIC());
400 //refresh_all_properties ();
401 if (m_lookup_table_always_on) {
402 show_lookup_table ();
404 hide_lookup_table ();
406 m_pv->updateWindows(CIMIView::PREEDIT_MASK | CIMIView::CANDIDATE_MASK);
410 SunPyInstance::focus_in ()
412 SCIM_DEBUG_IMENGINE(3) << get_id() << ": focus_in ()\n";
414 //show_lookup_table ();
415 initialize_all_properties ();
417 hide_preedit_string ();
418 //hide_aux_string ();
420 init_lookup_table_labels ();
422 //hide_aux_string ();
424 m_pv->updateWindows(CIMIView::PREEDIT_MASK | CIMIView::CANDIDATE_MASK);
428 SunPyInstance::focus_out ()
430 SCIM_DEBUG_IMENGINE(3) << get_id() << ": focus_out ()\n";
435 SunPyInstance::flush ()
437 SCIM_DEBUG_IMENGINE(3) << get_id() << ": flush ()\n";
438 m_pv->onCandidateSelectRequest(0);
442 SunPyInstance::trigger_property (const String &property)
444 SCIM_DEBUG_IMENGINE (3) << get_id() << ": trigger_property(" << property << ")\n";
446 if (property == SCIM_PROP_STATUS) {
447 const int is_CN = m_pv->getStatusAttrValue(CIMIWinHandler::STATUS_ID_CN);
448 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN, is_CN?0:1);
449 } else if (property == SCIM_PROP_LETTER) {
450 const int is_fullsymbol = m_pv->getStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL);
451 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL, is_fullsymbol?0:1);
452 } else if (property == SCIM_PROP_PUNCT) {
453 const int is_fullpunc = m_pv->getStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC);
454 m_pv->setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, is_fullpunc?0:1);
459 SunPyInstance::set_layout (unsigned int layout)
463 case ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL:
464 case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER:
465 case ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL:
466 case ECORE_IMF_INPUT_PANEL_LAYOUT_URL:
467 show_lookup_table ();
468 m_lookup_table_always_on = true;
470 case ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER:
471 case ECORE_IMF_INPUT_PANEL_LAYOUT_IP:
472 case ECORE_IMF_INPUT_PANEL_LAYOUT_MONTH:
473 case ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY:
474 hide_lookup_table ();
475 m_lookup_table_always_on = false;
481 SunPyInstance::init_lookup_table_labels ()
483 m_pv->setCandiWindowSize(10);
484 m_lookup_table->set_page_size (10);
485 m_lookup_table->show_cursor ();
489 SunPyInstance::initialize_all_properties ()
491 PropertyList proplist;
493 proplist.push_back (_status_property);
494 proplist.push_back (_letter_property);
495 proplist.push_back (_punct_property);
497 register_properties (proplist);
498 refresh_all_properties ();
502 SunPyInstance::refresh_all_properties ()
504 SCIM_DEBUG_IMENGINE (3) << get_id() << ": refresh_all_properties()\n";
505 m_wh->updateStatus(CIMIWinHandler::STATUS_ID_CN,
506 m_pv->getStatusAttrValue(CIMIWinHandler::STATUS_ID_CN));
507 m_wh->updateStatus(CIMIWinHandler::STATUS_ID_FULLPUNC,
508 m_pv->getStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC));
509 m_wh->updateStatus(CIMIWinHandler::STATUS_ID_FULLSYMBOL,
510 m_pv->getStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL));
515 SunPyInstance::refresh_status_property(bool cn)
517 SCIM_DEBUG_IMENGINE (3) << get_id() << ": refresh_status_property(" << cn << ")\n";
521 _status_property.set_label(cn ? "中" : "英");
522 update_property(_status_property);
526 SunPyInstance::refresh_fullsymbol_property(bool full)
528 SCIM_DEBUG_IMENGINE (3) << get_id() << ": refresh_fullsimbol_property(" << full << ")\n";
529 _letter_property.set_icon(
530 full ? SCIM_FULL_LETTER_ICON : SCIM_HALF_LETTER_ICON);
531 update_property(_letter_property);
535 SunPyInstance::refresh_fullpunc_property(bool full)
537 _punct_property.set_icon(
538 full ? SCIM_FULL_PUNCT_ICON : SCIM_HALF_PUNCT_ICON);
539 update_property(_punct_property);
543 SunPyInstance::create_session(CHotkeyProfile *hotkey_profile)
545 SCIM_DEBUG_IMENGINE (3) << get_id() << ": create_session()\n";
547 CSunpinyinSessionFactory& factory = CSunpinyinSessionFactory::getFactory();
548 factory.setPinyinScheme(CSunpinyinSessionFactory::QUANPIN);
549 factory.setCandiWindowSize(10);
550 m_pv = factory.createSession();
552 SCIM_DEBUG_IMENGINE (3) << get_id() << " factory.createSession() failed\n";
556 m_pv->setHotkeyProfile(hotkey_profile);
558 m_lookup_table = new SunLookupTable();
559 m_common_lookup_table = new CommonLookupTable();
560 for(int i = 0;i < sizeof(g_common_symbol)/sizeof(char);i++) {
561 char _str[2]={g_common_symbol[i],0};
562 m_common_lookup_table->append_candidate (utf8_mbstowcs ((const char*)_str),attrs);
564 m_wh = new CScimWinHandler(this, m_lookup_table);
565 m_pv->attachWinHandler(m_wh);
569 SunPyInstance::destroy_session()
571 SCIM_DEBUG_IMENGINE (3) << get_id() << ": destroy_session()\n";
573 // wh and ic are not pointers, I don't think it's necessary to delete them
577 delete m_lookup_table;
578 delete m_common_lookup_table;
586 SunPyInstance::build_preedit_attribs (const IPreeditString* ppd)
589 const int sz = ppd->charTypeSize();
590 for (int i = 0; i < sz; ) {
591 const int ct = ppd->charTypeAt(i);
592 if (ct & IPreeditString::ILLEGAL) {
594 for (++i; (i<sz) && (ppd->charTypeAt(i) & IPreeditString::ILLEGAL); ++i) ;
595 attrs.push_back( Attribute(start, i-start,
596 SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_REVERSE));
597 } else if (ct & IPreeditString::NORMAL_CHAR) {
598 if (ct & IPreeditString::USER_CHOICE) {
600 for (++i; (i<sz) && (ppd->charTypeAt(i) & IPreeditString::USER_CHOICE); ++i) ;
601 attrs.push_back( Attribute(start, i-start,
602 SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_UNDERLINE));
614 SunPyInstance::redraw_preedit_string (const IPreeditString* ppd)
616 SCIM_DEBUG_IMENGINE (3) << get_id() << ": redraw_preedit_string()\n";
617 if (ppd->size() != 0) {
619 const int caret = ppd->caret();
620 if (caret > 0 && caret <= ppd->size()) {
621 attrs.push_back( Attribute(ppd->candi_start(),
623 SCIM_ATTR_DECORATE, SCIM_ATTR_DECORATE_REVERSE));
625 update_preedit_string( wstr_to_widestr(ppd->string(), ppd->size()) );
626 show_preedit_string ();
627 update_preedit_caret (caret);
629 hide_preedit_string ();
634 SunPyInstance::redraw_lookup_table(const ICandidateList* pcl)
636 SCIM_DEBUG_IMENGINE (3) << get_id() << ": redraw_lookup_table()\n";
638 m_lookup_table->update(*pcl);
639 if (m_lookup_table->number_of_candidates()) {
640 update_lookup_table(*m_lookup_table);
642 update_lookup_table(*m_common_lookup_table);
647 SunPyInstance::reload_config(const ConfigPointer &config)
649 SCIM_DEBUG_IMENGINE (3) << get_id() << ": reload_config()\n";
651 if (m_factory->valid()) {
652 m_factory->load_user_config();