/**
 * @ingroup hangulic
 * @brief 키 입력을 처리하여 실제로 한글 조합을 하는 함수
 * @param hic @ref HangulInputContext 오브젝트
 * @param ascii 키 이벤트
 * @return @ref HangulInputContext 가 이 키를 사용했으면 true,
 *	     사용하지 않았으면 false
 *
 * ascii 값으로 주어진 키 이벤트를 받아서 내부의 한글 조합 상태를
 * 변화시키고, preedit, commit 스트링을 저장한다.
 *
 * libhangul의 키 이벤트 프로세스는 ASCII 코드 값을 기준으로 처리한다.
 * 이 키 값은 US Qwerty 자판 배열에서의 키 값에 해당한다.
 * 따라서 유럽어 자판을 사용하는 경우에는 해당 키의 ASCII 코드를 직접
 * 전달하면 안되고, 그 키가 US Qwerty 자판이었을 경우에 발생할 수 있는 
 * ASCII 코드 값을 주어야 한다.
 * 또한 ASCII 코드 이므로 Shift 상태는 대문자로 전달이 된다.
 * Capslock이 눌린 경우에는 대소문자를 뒤바꾸어 보내주지 않으면 
 * 마치 Shift가 눌린 것 처럼 동작할 수 있으므로 주의한다.
 * preedit, commit 스트링은 hangul_ic_get_preedit_string(),
 * hangul_ic_get_commit_string() 함수를 이용하여 구할 수 있다.
 * 
 * 이 함수의 사용법에 대한 설명은 @ref hangulicusage 부분을 참조한다.
 *
 * @remarks 이 함수는 @ref HangulInputContext 의 상태를 변화 시킨다.
 */
bool
hangul_ic_process(HangulInputContext *hic, int ascii)
{
    ucschar c;

    if (hic == NULL)
	return false;

    hic->preedit_string[0] = 0;
    hic->commit_string[0] = 0;

    c = hangul_keyboard_get_mapping(hic->keyboard, 0, ascii);
    if (hic->on_translate != NULL)
	hic->on_translate(hic, ascii, &c, hic->on_translate_data);

    if (ascii == '\b') {
	return hangul_ic_backspace(hic);
    }

    int type = hangul_keyboard_get_type(hic->keyboard);
    switch (type) {
    case HANGUL_KEYBOARD_TYPE_JASO:
    case HANGUL_KEYBOARD_TYPE_JASO_YET:
	return hangul_ic_process_jaso(hic, c);
    case HANGUL_KEYBOARD_TYPE_ROMAJA:
	return hangul_ic_process_romaja(hic, ascii, c);
    default:
	return hangul_ic_process_jamo(hic, c);
    }
}
bool QHangulPlatformInputContext::backspace() {
    bool ret = hangul_ic_backspace(m_hic);
    if (ret) {
	QString str = getPreeditString();
	updatePreedit(str);
    }
    return ret;
}
Beispiel #3
0
static PyObject *_pyhangulic_backspace(PY_HANGULIC *self, PyObject *args)
{
    int ret;

    ret = hangul_ic_backspace(self->hic);

    return Py_BuildValue("i", ret);
}
Beispiel #4
0
END_TEST

START_TEST(test_hangul_ic_process_romaja)
{
    HangulInputContext* ic;
    const ucschar* preedit;
    const ucschar* commit;

    // romaja keyboard test
    ic = hangul_ic_new("ro");

    // basic test: han produces 한
    hangul_ic_process(ic, 'h');
    hangul_ic_process(ic, 'a');
    hangul_ic_process(ic, 'n');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == L'한');
    fail_unless(commit[0] == 0);

    hangul_ic_reset(ic);

    // insert ㅇ when a syllable is not started with consonant
    hangul_ic_process(ic, 'a');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == L'아');
    fail_unless(commit[0] == 0);

    // remove correctly when automatically ㅇ was inserted
    hangul_ic_backspace(ic);

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == 0);
    fail_unless(commit[0] == 0);

    // append ㅡ when a syllable is not ended with vowel
    hangul_ic_process(ic, 't');
    hangul_ic_process(ic, 't');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == 0x314c); // ㅌ
    fail_unless(commit[0] == L'트');

    // ng makes trailing ㅇ
    hangul_ic_reset(ic);
    hangul_ic_process(ic, 'g');
    hangul_ic_process(ic, 'a');
    hangul_ic_process(ic, 'n');
    hangul_ic_process(ic, 'g');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == L'강'); // 강
    fail_unless(commit[0] == 0);

    // gangi makes 강이
    hangul_ic_process(ic, 'i');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == L'이');
    fail_unless(commit[0] == L'강');  // 강

    // nanG makes 난ㄱ
    // uppercase makes new syllable
    hangul_ic_process(ic, 'n');
    hangul_ic_process(ic, 'a');
    hangul_ic_process(ic, 'n');
    hangul_ic_process(ic, 'G');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == 0x3131); // ㄱ
    fail_unless(commit[0] == L'난');  // 난

    // special operation for x
    // x generate ㅈ for leading consonant
    hangul_ic_reset(ic);
    hangul_ic_process(ic, 'x');
    hangul_ic_process(ic, 'x');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == 0x3148); // 지
    fail_unless(commit[0] == L'즈');

    hangul_ic_reset(ic);
    hangul_ic_process(ic, 'x');
    hangul_ic_process(ic, 'y');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == L'지'); // 지
    fail_unless(commit[0] == 0x0);

    // x generate ㄱㅅ for trailing consonant
    // and ㅅ will be transferred to next syllable when next input
    // character is vowel.
    hangul_ic_reset(ic);
    hangul_ic_process(ic, 's');
    hangul_ic_process(ic, 'e');
    hangul_ic_process(ic, 'x');
    hangul_ic_process(ic, 'y');

    preedit = hangul_ic_get_preedit_string(ic);
    commit = hangul_ic_get_commit_string(ic);
    fail_unless(preedit[0] == L'시'); // 시
    fail_unless(commit[0] == L'섹');  // 섹
    
    hangul_ic_delete(ic);
}
bool
HangulInstance::process_key_event (const KeyEvent& rawkey)
{
    SCIM_DEBUG_IMENGINE(1) << "process_key_event.\n";

    KeyEvent key = rawkey.map_to_layout(SCIM_KEYBOARD_Default);

    m_prev_key = key;

    if (use_ascii_mode() && !is_hangul_mode()) {
	if (is_hangul_key(key)) {
	    toggle_hangul_mode();
	    return true;
	}

	return false;
    }

    /* ignore key release. */
    if (key.is_key_release ())
        return false;

    /* mode change */
    if (use_ascii_mode() && is_hangul_key(key)) {
	toggle_hangul_mode();
	return true;
    }

    /* hanja mode */
    if (is_hanja_mode_key (key)) {
	toggle_hanja_mode();
    }

    /* toggle candidate table */
    if (is_hanja_key (key)) {
	if (is_hanja_mode()) {
	    update_candidates ();
	} else {
	    if (m_lookup_table.number_of_candidates ())
		delete_candidates ();
	    else
		update_candidates ();
	}

        return true;
    }

    /* ignore shift keys */
    if (key.code == SCIM_KEY_Shift_L || key.code == SCIM_KEY_Shift_R)
        return false;

    /* flush on modifier-on keys */
    if (key.is_control_down() || key.is_alt_down()) {
	flush ();
        return false;
    }

    /* candidate keys */
    if (m_lookup_table.number_of_candidates ()) {
        if (candidate_key_event(key))
	    return true;
    }

    /* change to ascii mode on ESCAPE key, for vi users.
     * We should process this key after processing candidate keys,
     * or input mode will be changed to non-hangul mode when the user presses
     * escape key to close candidate window. */
    if (use_ascii_mode() && !is_hanja_mode()) {
	if (key.code == SCIM_KEY_Escape) {
	    toggle_hangul_mode();
	}
    }

    /* backspace */
    if (is_backspace_key(key)) {
        bool ret = hangul_ic_backspace(m_hic);
        if (ret) {
	    hangul_update_preedit_string ();
	} else if (m_preedit.length() > 0) {
	    ret = true;
	    m_preedit.erase(m_preedit.length() - 1, 1);
	    hangul_update_preedit_string();
	} else {
	    if (m_surrounding_text.length() > 0) {
		m_surrounding_text.erase(m_surrounding_text.length() - 1, 1);
		if (m_surrounding_text.empty()) {
		    delete_candidates();
		    return ret;
		}
	    }
	}

	if (is_hanja_mode() && m_lookup_table.number_of_candidates()) {
	    update_candidates();
	}

        return ret;
    }

    if (key.code >= SCIM_KEY_exclam && key.code <= SCIM_KEY_asciitilde) {
	/* main hangul composing process */
	int ascii = key.get_ascii_code();
	if (key.is_caps_lock_down()) {
	    if (isupper(ascii))
		ascii = tolower(ascii);
	    else if (islower(ascii))
		ascii = toupper(ascii);
	}

	bool ret = hangul_ic_process(m_hic, ascii);

	WideString wstr;
	wstr = get_commit_string ();
	if (wstr.length ()) {
	    /* Before commit, we set preedit string to null to work arround
	     * some buggy IM implementation, ex) Qt, Evolution */
	    hide_preedit_string ();
	    if (is_hanja_mode() || m_factory->m_commit_by_word) {
		m_preedit += wstr;
	    } else {
		commit_string(wstr);
	    }
	}

	if (is_hanja_mode() || m_factory->m_commit_by_word) {
	    if (hangul_ic_is_empty(m_hic)) {
		flush();
	    }
	}

	hangul_update_preedit_string ();

	if (is_hanja_mode()) {
	    update_candidates();
	}

	return ret;
    }

    flush();
    return false;
}
Beispiel #6
0
static gboolean
ibus_hangul_engine_process_key_event (IBusEngine     *engine,
                                      guint           keyval,
                                      guint           keycode,
                                      guint           modifiers)
{
    IBusHangulEngine *hangul = (IBusHangulEngine *) engine;

    guint mask;
    gboolean retval;
    const ucschar *str;

    if (modifiers & IBUS_RELEASE_MASK)
        return FALSE;

    // if we don't ignore shift keys, shift key will make flush the preedit 
    // string. So you cannot input shift+key.
    // Let's think about these examples:
    //   dlTek (2 set)
    //   qhRdmaqkq (2 set)
    if (keyval == IBUS_Shift_L || keyval == IBUS_Shift_R)
        return FALSE;

    // If hanja key has any modifiers, we ignore that modifier keyval,
    // or we cannot make the hanja key work.
    // Because when we get the modifier key alone, we commit the
    // current preedit string. So after that, even if we get the
    // right hanja key event, we don't have preedit string to be changed
    // to hanja word.
    // See this bug: http://code.google.com/p/ibus/issues/detail?id=1036
    if (hanja_key_list_has_modifier(&hanja_keys, keyval))
	return FALSE; 

    if (hanja_key_list_match(&hanja_keys, keyval, modifiers)) {
        if (hangul->hanja_list == NULL) {
            ibus_hangul_engine_update_lookup_table (hangul);
        } else {
            ibus_hangul_engine_hide_lookup_table (hangul);
        }
        return TRUE;
    }

    if (hangul->hanja_list != NULL) {
        retval = ibus_hangul_engine_process_candidate_key_event (hangul,
                     keyval, modifiers);
        if (hangul->hanja_mode) {
            if (retval)
                return TRUE;
        } else {
            return TRUE;
        }
    }

    // If we've got a key event with some modifiers, commit current
    // preedit string and ignore this key event.
    // So, if you want to add some key event handler, put it 
    // before this code.
    // Ignore key event with control, alt, super or mod5
    mask = IBUS_CONTROL_MASK |
	    IBUS_MOD1_MASK | IBUS_MOD3_MASK | IBUS_MOD4_MASK | IBUS_MOD5_MASK;
    if (modifiers & mask) {
        ibus_hangul_engine_flush (hangul);
        return FALSE;
    }

    if (keyval == IBUS_BackSpace) {
        retval = hangul_ic_backspace (hangul->context);
        if (!retval) {
            guint preedit_len = ustring_length (hangul->preedit);
            if (preedit_len > 0) {
                ustring_erase (hangul->preedit, preedit_len - 1, 1);
                retval = TRUE;
            }
        }

        ibus_hangul_engine_update_preedit_text (hangul);

        if (hangul->hanja_mode) {
            if (ibus_hangul_engine_has_preedit (hangul)) {
                ibus_hangul_engine_update_lookup_table (hangul);
            } else {
                ibus_hangul_engine_hide_lookup_table (hangul);
            }
        }
    } else {
	// We need to normalize the keyval to US qwerty keylayout,
	// because the korean input method is depend on the position of
	// each key, not the character. We make the keyval from keycode
	// as if the keyboard is US qwerty layout. Then we can assume the
	// keyval represent the position of the each key.
	// But if the hic is in transliteration mode, then we should not
	// normalize the keyval.
	bool is_transliteration_mode =
		 hangul_ic_is_transliteration(hangul->context);
	if (!is_transliteration_mode) {
	    if (keymap != NULL)
		keyval = ibus_keymap_lookup_keysym(keymap, keycode, modifiers);
	}

        // ignore capslock
        if (modifiers & IBUS_LOCK_MASK) {
            if (keyval >= 'A' && keyval <= 'z') {
                if (isupper(keyval))
                    keyval = tolower(keyval);
                else
                    keyval = toupper(keyval);
            }
        }
        retval = hangul_ic_process (hangul->context, keyval);

        str = hangul_ic_get_commit_string (hangul->context);
        if (word_commit || hangul->hanja_mode) {
            const ucschar* hic_preedit;

            hic_preedit = hangul_ic_get_preedit_string (hangul->context);
            if (hic_preedit != NULL && hic_preedit[0] != 0) {
                ustring_append_ucs4 (hangul->preedit, str, -1);
            } else {
                IBusText *text;
                const ucschar* preedit;

                ustring_append_ucs4 (hangul->preedit, str, -1);
                if (ustring_length (hangul->preedit) > 0) {
                    /* clear preedit text before commit */
                    ibus_hangul_engine_clear_preedit_text (hangul);

                    preedit = ustring_begin (hangul->preedit);
                    text = ibus_text_new_from_ucs4 ((gunichar*)preedit);
                    ibus_engine_commit_text (engine, text);
                }
                ustring_clear (hangul->preedit);
            }
        } else {
            if (str != NULL && str[0] != 0) {
                IBusText *text;

                /* clear preedit text before commit */
                ibus_hangul_engine_clear_preedit_text (hangul);

                text = ibus_text_new_from_ucs4 (str);
                ibus_engine_commit_text (engine, text);
            }
        }

        ibus_hangul_engine_update_preedit_text (hangul);

        if (hangul->hanja_mode) {
            ibus_hangul_engine_update_lookup_table (hangul);
        }

        if (!retval)
            ibus_hangul_engine_flush (hangul);
    }

    return retval;
}
Beispiel #7
0
gboolean
nimf_libhangul_filter_event (NimfEngine    *engine,
                             NimfServiceIC *target,
                             NimfEvent     *event)
{
  g_debug (G_STRLOC ": %s", G_STRFUNC);

  guint    keyval;
  gboolean retval = FALSE;

  NimfLibhangul *hangul = NIMF_LIBHANGUL (engine);

  if (event->key.type   == NIMF_EVENT_KEY_RELEASE ||
      event->key.keyval == NIMF_KEY_Shift_L       ||
      event->key.keyval == NIMF_KEY_Shift_R)
    return FALSE;

  if (event->key.state & (NIMF_CONTROL_MASK | NIMF_MOD1_MASK))
  {
    nimf_libhangul_reset (engine, target);
    return FALSE;
  }

  if (G_UNLIKELY (nimf_event_matches (event,
                  (const NimfKey **) hangul->hanja_keys)))
  {
    if (nimf_candidatable_is_visible (hangul->candidatable) == FALSE)
    {
      gchar item[4];
      const char *key = hangul->preedit_string;
      gboolean use_preedit;

      if (hangul->preedit_string[0] == 0)
      {
        gchar *text;
        gint   cursor_pos;

        nimf_engine_get_surrounding (engine, target, &text, &cursor_pos);

        if (text && cursor_pos > 0)
        {
          gchar *p = g_utf8_offset_to_pointer (text, cursor_pos - 1);
          g_utf8_strncpy (item, p, 1);

          if (g_utf8_validate (item, -1, NULL))
            key = item;
        }

        g_free (text);
      }

      hanja_list_delete (hangul->hanja_list);
      nimf_candidatable_clear (hangul->candidatable, target);
      hangul->hanja_list = hanja_table_match_exact (nimf_libhangul_hanja_table, key);

      if (hangul->hanja_list == NULL)
        hangul->hanja_list = hanja_table_match_exact (nimf_libhangul_symbol_table, key);

      hangul->n_pages = (hanja_list_get_size (hangul->hanja_list) + 9) / 10;
      hangul->current_page = 1;
      nimf_libhangul_update_page (engine, target);
      use_preedit = nimf_service_ic_get_use_preedit (target);

      if (!use_preedit)
        nimf_candidatable_set_auxiliary_text (hangul->candidatable,
                                              key, g_utf8_strlen (key, -1));

      nimf_candidatable_show (hangul->candidatable, target, !use_preedit);
      nimf_candidatable_select_first_item_in_page (hangul->candidatable);
    }
    else
    {
      nimf_candidatable_hide (hangul->candidatable);
      nimf_candidatable_clear (hangul->candidatable, target);
      hanja_list_delete (hangul->hanja_list);
      hangul->hanja_list = NULL;
      hangul->current_page = 0;
      hangul->n_pages = 0;
    }

    return TRUE;
  }

  if (nimf_candidatable_is_visible (hangul->candidatable))
  {
    switch (event->key.keyval)
    {
      case NIMF_KEY_Return:
      case NIMF_KEY_KP_Enter:
        {
          gchar *text;

          text = nimf_candidatable_get_selected_text (hangul->candidatable);
          on_candidate_clicked (engine, target, text, -1);

          g_free (text);
        }
        break;
      case NIMF_KEY_Up:
      case NIMF_KEY_KP_Up:
        nimf_candidatable_select_previous_item (hangul->candidatable);
        break;
      case NIMF_KEY_Down:
      case NIMF_KEY_KP_Down:
        nimf_candidatable_select_next_item (hangul->candidatable);
        break;
      case NIMF_KEY_Page_Up:
      case NIMF_KEY_KP_Page_Up:
        nimf_libhangul_page_up (engine, target);
        break;
      case NIMF_KEY_Page_Down:
      case NIMF_KEY_KP_Page_Down:
        nimf_libhangul_page_down (engine, target);
        break;
      case NIMF_KEY_Home:
        nimf_libhangul_page_home (engine, target);
        break;
      case NIMF_KEY_End:
        nimf_libhangul_page_end (engine, target);
        break;
      case NIMF_KEY_Escape:
        nimf_candidatable_hide (hangul->candidatable);
        break;
      case NIMF_KEY_0:
      case NIMF_KEY_1:
      case NIMF_KEY_2:
      case NIMF_KEY_3:
      case NIMF_KEY_4:
      case NIMF_KEY_5:
      case NIMF_KEY_6:
      case NIMF_KEY_7:
      case NIMF_KEY_8:
      case NIMF_KEY_9:
      case NIMF_KEY_KP_0:
      case NIMF_KEY_KP_1:
      case NIMF_KEY_KP_2:
      case NIMF_KEY_KP_3:
      case NIMF_KEY_KP_4:
      case NIMF_KEY_KP_5:
      case NIMF_KEY_KP_6:
      case NIMF_KEY_KP_7:
      case NIMF_KEY_KP_8:
      case NIMF_KEY_KP_9:
        {
          if (hangul->hanja_list == NULL || hangul->current_page < 1)
            break;

          gint i, n;
          gint list_len = hanja_list_get_size (hangul->hanja_list);

          if (event->key.keyval >= NIMF_KEY_0 &&
              event->key.keyval <= NIMF_KEY_9)
            n = (event->key.keyval - NIMF_KEY_0 + 9) % 10;
          else if (event->key.keyval >= NIMF_KEY_KP_0 &&
                   event->key.keyval <= NIMF_KEY_KP_9)
            n = (event->key.keyval - NIMF_KEY_KP_0 + 9) % 10;
          else
            break;

          i = (hangul->current_page - 1) * 10 + n;

          if (i < MIN (hangul->current_page * 10, list_len))
          {
            const Hanja *hanja = hanja_list_get_nth (hangul->hanja_list, i);
            const char  *text = hanja_get_value (hanja);
            on_candidate_clicked (engine, target, (gchar *) text, -1);
          }
        }
        break;
      default:
        break;
    }

    return TRUE;
  }

  const ucschar *ucs_commit;
  const ucschar *ucs_preedit;

  if (G_UNLIKELY (event->key.keyval == NIMF_KEY_BackSpace))
  {
    retval = hangul_ic_backspace (hangul->context);

    if (retval)
    {
      ucs_preedit = hangul_ic_get_preedit_string (hangul->context);
      gchar *new_preedit = g_ucs4_to_utf8 (ucs_preedit, -1, NULL, NULL, NULL);
      nimf_libhangul_update_preedit (engine, target, new_preedit);
    }

    return retval;
  }

  if (G_UNLIKELY (g_strcmp0 (hangul->method, "ro") == 0))
    keyval = event->key.keyval;
  else
    keyval = nimf_event_keycode_to_qwerty_keyval (event);

  if (!hangul->is_double_consonant_rule &&
      (g_strcmp0 (hangul->method, "2") == 0) &&
      nimf_libhangul_filter_leading_consonant (engine, target, keyval))
    return TRUE;

  retval = hangul_ic_process (hangul->context, keyval);

  ucs_commit  = hangul_ic_get_commit_string  (hangul->context);
  ucs_preedit = hangul_ic_get_preedit_string (hangul->context);

  gchar *new_commit  = g_ucs4_to_utf8 (ucs_commit,  -1, NULL, NULL, NULL);

  if (ucs_commit[0] != 0)
    nimf_libhangul_emit_commit (engine, target, new_commit);

  g_free (new_commit);

  gchar *new_preedit = g_ucs4_to_utf8 (ucs_preedit, -1, NULL, NULL, NULL);
  nimf_libhangul_update_preedit (engine, target, new_preedit);

  return retval;
}