1 /* vim:set et ts=4 sts=4:
3 * ibus-pinyin - The Chinese PinYin engine for IBus
5 * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "lua-plugin.h"
29 #include "PYPointer.h"
30 #include "PYLookupTable.h"
32 #include "PYDynamicSpecialPhrase.h"
35 #include "PYExtEditor.h"
40 /* Write digit/alpha/none Label generator here.
41 * foreach (results): 1, from get_retval; 2..n from get_retvals.
44 ExtEditor::ExtEditor (PinyinProperties & props, Config & config)
45 : Editor (props, config),
51 m_lua_plugin = ibus_engine_plugin_new ();
53 gchar * path = g_build_filename (g_get_user_config_dir (),
54 ".ibus", "pinyin", "base.lua", NULL);
55 loadLuaScript ( ".." G_DIR_SEPARATOR_S "lua" G_DIR_SEPARATOR_S "base.lua")||
56 loadLuaScript (path) ||
57 loadLuaScript (PKGDATADIR G_DIR_SEPARATOR_S "base.lua");
63 ExtEditor::loadLuaScript (std::string filename)
65 return !ibus_engine_plugin_load_lua_script (m_lua_plugin, filename.c_str ());
69 ExtEditor::resetLuaState ()
71 g_object_unref (m_lua_plugin);
72 m_lua_plugin = ibus_engine_plugin_new ();
77 ExtEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
79 //IBUS_SHIFT_MASK is removed.
80 modifiers &= (IBUS_CONTROL_MASK |
89 //handle backspace/delete here.
90 if (processEditKey (keyval))
93 //handle page/cursor up/down here.
94 if (processPageKey (keyval))
97 //handle label key select here.
98 if (processLabelKey (keyval))
101 if (processSpace (keyval))
104 if (processEnter (keyval))
107 m_cursor = std::min (m_cursor, (guint)m_text.length ());
109 /* Remember the input string. */
111 case 0: //Empty input string.
113 g_return_val_if_fail ( 'i' == keyval, FALSE);
114 if ( 'i' == keyval ) {
115 m_text.insert (m_cursor, keyval);
120 case 1 ... 2: // Only contains 'i' in input string.
122 g_return_val_if_fail ( 'i' == m_text[0], FALSE);
123 if ( isalnum (keyval) ) {
124 m_text.insert (m_cursor, keyval);
129 default: //Here is the appended argment.
131 g_return_val_if_fail ( 'i' == m_text[0], FALSE);
132 if (isprint (keyval)) {
133 m_text.insert (m_cursor, keyval);
139 /* Deal other staff with updateStateFromInput (). */
140 updateStateFromInput ();
146 ExtEditor::processEditKey (guint keyval)
152 updateStateFromInput ();
157 updateStateFromInput ();
165 ExtEditor::processPageKey (guint keyval)
168 //For 2000-10-10 16:30 input.
170 if (m_config.commaPeriodPage ()) {
177 if (m_config.minusEqualPage ()) {
184 if (m_config.commaPeriodPage ()) {
190 if (m_config.minusEqualPage ()) {
206 case IBUS_KP_Page_Up:
211 case IBUS_KP_Page_Down:
223 ExtEditor::processLabelKey (guint keyval)
225 //According to enum ExtEditorLabelMode.
228 case LABEL_LIST_DIGIT:
231 return selectCandidateInPage (keyval - '1');
234 return selectCandidateInPage (9);
238 case LABEL_LIST_NUMBERS:
239 case LABEL_LIST_ALPHA:
242 return selectCandidateInPage (keyval - 'a');
245 return selectCandidateInPage (keyval - 'A');
256 ExtEditor::processSpace (guint keyval)
258 if (!(keyval == IBUS_space || keyval == IBUS_KP_Space))
261 guint cursor_pos = m_lookup_table.cursorPos ();
264 case LABEL_LIST_NUMBERS:
265 selectCandidate (cursor_pos);
267 case LABEL_LIST_COMMANDS:
268 case LABEL_LIST_DIGIT:
269 case LABEL_LIST_ALPHA:
270 selectCandidate (cursor_pos);
272 case LABEL_LIST_SINGLE:
273 g_return_val_if_fail (cursor_pos == 0 , FALSE);
274 selectCandidate (cursor_pos);
283 ExtEditor::processEnter(guint keyval)
285 if (keyval != IBUS_Return)
288 if (m_text.length () == 0)
298 ExtEditor::pageUp (void)
300 if (G_LIKELY(m_lookup_table.pageUp ())) {
306 ExtEditor::pageDown (void)
308 if (G_LIKELY(m_lookup_table.pageDown ())) {
314 ExtEditor::removeCharBefore (void)
316 if (G_UNLIKELY( m_cursor <= 0 )) {
321 if (G_UNLIKELY( m_cursor > m_text.length () )) {
322 m_cursor = m_text.length ();
326 m_text.erase (m_cursor - 1, 1);
327 m_cursor = std::max (0, static_cast<int>(m_cursor) - 1);
332 ExtEditor::removeCharAfter (void)
334 if (G_UNLIKELY( m_cursor < 0 )) {
339 if (G_UNLIKELY( m_cursor >= m_text.length () )) {
340 m_cursor = m_text.length ();
343 m_text.erase (m_cursor, 1);
344 m_cursor = std::min (m_cursor, (guint)m_text.length ());
349 ExtEditor::cursorUp (void)
351 if (G_LIKELY (m_lookup_table.cursorUp ())) {
357 ExtEditor::cursorDown (void)
359 if (G_LIKELY (m_lookup_table.cursorDown ())) {
365 ExtEditor::update (void)
367 updateLookupTable ();
368 updatePreeditText ();
369 updateAuxiliaryText ();
373 ExtEditor::reset (void)
376 updateStateFromInput ();
381 ExtEditor::candidateClicked (guint index, guint button, guint state)
383 selectCandidateInPage (index);
387 ExtEditor::selectCandidateInPage (guint index)
389 guint page_size = m_lookup_table.pageSize ();
390 guint cursor_pos = m_lookup_table.cursorPos ();
392 if (G_UNLIKELY(index >= page_size))
394 index += (cursor_pos / page_size) * page_size;
396 return selectCandidate (index);
400 ExtEditor::selectCandidate (guint index)
403 case LABEL_LIST_NUMBERS:
405 if ( index >= m_lookup_table.size() )
408 IBusText * candidate = m_lookup_table.getCandidate(index);
409 Text text(candidate);
415 case LABEL_LIST_COMMANDS:
417 std::string prefix = m_text.substr (1, 2);
418 int len = prefix.length ();
419 const char * prefix_str = prefix.c_str ();
420 const GArray * commands = ibus_engine_plugin_get_available_commands (m_lua_plugin);
421 int match_count = -1;
422 for (int i = 0; i < static_cast<int>(commands->len); ++i) {
423 lua_command_t * command = &g_array_index (commands, lua_command_t, i);
424 if ( strncmp (prefix_str, command->command_name, len) == 0 ) {
427 if ( match_count == static_cast<int>(index) ) {
430 m_text += command->command_name;
431 m_cursor = m_text.length ();
435 updateStateFromInput ();
440 case LABEL_LIST_DIGIT:
441 case LABEL_LIST_ALPHA:
443 g_return_val_if_fail (m_result_num > 1, FALSE);
444 g_return_val_if_fail (static_cast<int>(index) < m_result_num, FALSE);
446 const lua_command_candidate_t * candidate = g_array_index (m_candidates, lua_command_candidate_t *, index);
447 if ( candidate->content ) {
448 Text text (candidate->content);
451 } else if (candidate->suggest) {
452 m_text += candidate->suggest;
453 m_cursor += strlen(candidate->suggest);
456 updateStateFromInput ();
461 case LABEL_LIST_SINGLE:
463 g_return_val_if_fail (m_result_num == 1, FALSE);
464 g_return_val_if_fail (index == 0, FALSE);
465 if ( m_candidate->content ) {
466 Text text (m_candidate->content);
469 } else if (m_candidate->suggest) {
470 m_text += m_candidate->suggest;
473 updateStateFromInput ();
485 ExtEditor::updateStateFromInput (void)
487 /* Do parse and candidates update here. */
488 /* prefix i double check here. */
489 if ( !m_text.length () ) {
491 m_auxiliary_text = "";
497 if ( ! 'i' == m_text[0] ) {
498 g_warning ("i is expected in m_text string.\n");
502 m_auxiliary_text = "i";
504 m_mode = LABEL_LIST_COMMANDS;
505 if ( 1 == m_text.length () ) {
506 fillCommandCandidates ();
509 /* Check m_text len, and update auxiliary string meanwhile.
510 * 1. only "i", dispatch to fillCommandCandidates (void).
511 * 2. "i" with one charactor,
512 * dispatch to fillCommandCandidates (std::string).
513 * 3. "i" with two charactor or more,
514 * dispatch to fillCommand (std::string, const char * argument).
517 if ( isalpha (m_text[1])) {
518 m_mode = LABEL_LIST_COMMANDS;
519 if ( m_text.length () == 2) {
520 fillCommandCandidates (m_text.substr (1,1).c_str ());
522 m_auxiliary_text += " ";
523 m_auxiliary_text += m_text.substr (1, 1);
525 } else if ( m_text.length () >= 3) {
526 std::string command_name = m_text.substr (1,2);
528 m_auxiliary_text += " ";
529 m_auxiliary_text += m_text.substr (1,2);
531 const char * argment = NULL;
532 std::string arg = "";
533 if (m_text.length () > 3) {
534 arg = m_text.substr (3);
535 argment = arg.c_str ();
536 m_auxiliary_text += " ";
537 m_auxiliary_text += argment;
539 /* finish auxiliary text computing here. */
541 const lua_command_t * command = ibus_engine_plugin_lookup_command (m_lua_plugin, command_name.c_str ());
542 if ( NULL == command) {
545 m_lookup_table.clear ();
549 if ( command->help ){
550 int space_len = std::max ( 0, m_aux_text_len
551 - (int) g_utf8_strlen (command->help, -1)
552 - 2 /* length of "[...]" */);
553 m_auxiliary_text.append(space_len, ' ');
555 m_auxiliary_text += "[";
556 m_auxiliary_text += command->help;
557 m_auxiliary_text += "]";
560 std::string label = command->leading;
562 if ( "digit" == label )
563 m_mode = LABEL_LIST_DIGIT;
564 else if ( "alpha" == label )
565 m_mode = LABEL_LIST_ALPHA;
567 m_mode = LABEL_LIST_NONE;
569 fillCommand (command_name, argment);
572 else if ( isdigit (m_text[1]) ) {
573 m_mode = LABEL_LIST_NUMBERS;
574 std::string number = m_text.substr(1);
575 m_auxiliary_text += " ";
576 m_auxiliary_text += number;
578 //Generate Chinese number.
579 gint64 num = atoll (number.c_str ());
580 fillChineseNumber (num);
587 ExtEditor::fillCommandCandidates (void)
589 return fillCommandCandidates ("");
593 ExtEditor::fillCommandCandidates (std::string prefix)
597 /* fill candidates here. */
598 int len = prefix.length ();
599 const char * prefix_str = prefix.c_str ();
600 const GArray * commands = ibus_engine_plugin_get_available_commands (m_lua_plugin);
602 for ( int i = 0; i < static_cast<int>(commands->len); ++i) {
603 lua_command_t * command = &g_array_index (commands, lua_command_t, i);
604 if ( strncmp (prefix_str, command->command_name, len) == 0) {
606 std::string candidate = command->command_name;
608 candidate += command->description;
609 m_lookup_table.setLabel (count, Text (""));
610 m_lookup_table.appendCandidate (Text (candidate));
618 ExtEditor::fillCommand (std::string command_name, const char * argument)
620 const lua_command_t * command = ibus_engine_plugin_lookup_command (m_lua_plugin, command_name.c_str ());
621 if ( NULL == command )
624 if ( m_result_num != 0) {
625 if ( m_result_num == 1) {
626 ibus_engine_plugin_free_candidate ((lua_command_candidate_t *)m_candidate);
629 for ( int i = 0; i < m_result_num; ++i) {
630 const lua_command_candidate_t * candidate = g_array_index (m_candidates, lua_command_candidate_t *, i);
631 ibus_engine_plugin_free_candidate ((lua_command_candidate_t *)candidate);
634 g_array_free (m_candidates, TRUE);
638 g_assert (m_candidates == NULL && m_candidate == NULL);
641 m_result_num = ibus_engine_plugin_call (m_lua_plugin, command->lua_function_name, argument);
643 if ( 1 == m_result_num )
644 m_mode = LABEL_LIST_SINGLE;
648 //Generate labels according to m_mode
649 if ( LABEL_LIST_DIGIT == m_mode ) {
650 for ( int i = 1; i <= 10; ++i )
651 m_lookup_table.setLabel ( i - 1, Text (i - 1 + '1') );
654 if ( LABEL_LIST_ALPHA == m_mode) {
655 for ( int i = 1; i <= 10; ++i )
656 m_lookup_table.setLabel ( i - 1, Text (i - 1 + 'a') );
659 if ( LABEL_LIST_NONE == m_mode || LABEL_LIST_SINGLE == m_mode) {
660 for ( int i = 1; i <= 10; ++i)
661 m_lookup_table.setLabel ( i - 1, Text (""));
664 //Generate candidates
666 if ( 1 == m_result_num ) {
667 m_candidate = ibus_engine_plugin_get_retval (m_lua_plugin);
669 if ( m_candidate->content ) {
670 result = m_candidate->content;
671 if (strstr (result.c_str (), "\n"))
674 if ( m_candidate->suggest && m_candidate-> help ) {
675 result += m_candidate->suggest;
678 result += m_candidate->help;
682 m_lookup_table.appendCandidate (Text (result));
683 }else if (m_result_num > 1) {
684 m_candidates = ibus_engine_plugin_get_retvals (m_lua_plugin);
685 for ( int i = 0; i < m_result_num; ++i) {
686 const lua_command_candidate_t * candidate = g_array_index (m_candidates, lua_command_candidate_t *, i);
688 if ( candidate->content ) {
689 result = candidate->content;
690 if (strstr (result.c_str (), "\n"))
693 if ( candidate->suggest && candidate-> help ) {
694 result += candidate->suggest;
697 result += candidate->help;
701 m_lookup_table.appendCandidate (Text (result));
709 ExtEditor::fillChineseNumber(gint64 num)
713 DynamicSpecialPhrase phrase ("", 0);
715 if ( LABEL_LIST_NUMBERS == m_mode) {
716 for ( int i = 1; i <= 10; ++i )
717 m_lookup_table.setLabel ( i - 1, Text (i - 1 + 'a') );
720 std::string result = phrase.simplified_number(num);
721 if ( !result.empty() ){
723 m_lookup_table.appendCandidate(text);
726 result = phrase.traditional_number(num);
727 if ( !result.empty() ){
729 m_lookup_table.appendCandidate(text);
732 result = phrase.simplest_cn_number(num);
733 if ( !result.empty() ){
735 m_lookup_table.appendCandidate(text);
742 ExtEditor::clearLookupTable (void)
744 m_lookup_table.clear ();
745 m_lookup_table.setPageSize (m_config.pageSize ());
746 m_lookup_table.setOrientation (m_config.orientation ());
750 ExtEditor::updateLookupTable (void)
752 if (m_lookup_table.size ()) {
753 Editor::updateLookupTableFast (m_lookup_table, TRUE);
761 ExtEditor::updatePreeditText (void)
763 if ( G_UNLIKELY(m_preedit_text.empty ()) ) {
768 StaticText preedit_text (m_preedit_text);
769 Editor::updatePreeditText (preedit_text, m_cursor, TRUE);
773 ExtEditor::updateAuxiliaryText (void)
775 if ( G_UNLIKELY(m_auxiliary_text.empty ()) ) {
776 hideAuxiliaryText ();
780 StaticText aux_text (m_auxiliary_text);
781 Editor::updateAuxiliaryText (aux_text, TRUE);