/* vim:set et ts=4 sts=4: * * ibus-libpinyin - Intelligent Pinyin engine based on libpinyin for IBus * * Copyright (c) 2011 Peng Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "PYPPhoneticEditor.h" #include "PYConfig.h" #include "PYPinyinProperties.h" #include "PYSimpTradConverter.h" using namespace PY; /* init static members */ PhoneticEditor::PhoneticEditor (PinyinProperties &props, Config &config): Editor (props, config), m_pinyin_len (0), m_lookup_table (m_config.pageSize ()) { } PhoneticEditor::~PhoneticEditor (){ } gboolean PhoneticEditor::processSpace (guint keyval, guint keycode, guint modifiers) { if (!m_text) return FALSE; if (cmshm_filter (modifiers) != 0) return TRUE; if (m_lookup_table.size () != 0) { selectCandidate (m_lookup_table.cursorPos ()); update (); } else { commit (); } return TRUE; } gboolean PhoneticEditor::processFunctionKey (guint keyval, guint keycode, guint modifiers) { if (m_text.empty ()) return FALSE; /* ignore numlock */ modifiers = cmshm_filter (modifiers); if (modifiers != 0 && modifiers != IBUS_CONTROL_MASK) return TRUE; /* process some cursor control keys */ if (modifiers == 0) { /* no modifiers. */ switch (keyval) { case IBUS_Return: case IBUS_KP_Enter: commit (m_text.c_str ()); reset (); return TRUE; case IBUS_BackSpace: removeCharBefore (); return TRUE; case IBUS_Delete: case IBUS_KP_Delete: removeCharAfter (); return TRUE; case IBUS_Left: case IBUS_KP_Left: moveCursorLeft (); return TRUE; case IBUS_Right: case IBUS_KP_Right: moveCursorRight (); return TRUE; case IBUS_Home: case IBUS_KP_Home: moveCursorToBegin (); return TRUE; case IBUS_End: case IBUS_KP_End: moveCursorToEnd (); return TRUE; case IBUS_Up: case IBUS_KP_Up: cursorUp (); return TRUE; case IBUS_Down: case IBUS_KP_Down: cursorDown (); return TRUE; case IBUS_Page_Up: case IBUS_KP_Page_Up: pageUp (); return TRUE; case IBUS_Page_Down: case IBUS_KP_Page_Down: case IBUS_Tab: pageDown (); return TRUE; case IBUS_Escape: reset (); return TRUE; default: return TRUE; } } else { /* ctrl key pressed. */ switch (keyval) { case IBUS_BackSpace: removeWordBefore (); return TRUE; case IBUS_Delete: case IBUS_KP_Delete: removeWordAfter (); return TRUE; case IBUS_Left: case IBUS_KP_Left: moveCursorLeftByWord (); return TRUE; case IBUS_Right: case IBUS_KP_Right: moveCursorRightByWord (); return TRUE; /* remove user phrase */ case IBUS_D: { guint index = m_lookup_table.cursorPos (); lookup_candidate_t * candidate = NULL; pinyin_get_candidate (m_instance, index, &candidate); if (pinyin_is_user_candidate (m_instance, candidate)) { pinyin_remove_user_candidate (m_instance, candidate); updatePinyin (); update (); } return TRUE; } default: return TRUE; } } return TRUE; } gboolean PhoneticEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers) { return FALSE; } void PhoneticEditor::updateLookupTableFast (void) { Editor::updateLookupTableFast (m_lookup_table, TRUE); } void PhoneticEditor::updateLookupTable (void) { m_lookup_table.clear (); fillLookupTable (); if (m_lookup_table.size()) { Editor::updateLookupTable (m_lookup_table, TRUE); } else { hideLookupTable (); } } #if 0 gboolean PhoneticEditor::fillLookupTableByPage (void) { guint len = 0; pinyin_get_n_candidate (m_instance, &len); guint filled_nr = m_lookup_table.size (); guint page_size = m_lookup_table.pageSize (); /* fill lookup table by libpinyin get candidates. */ guint need_nr = MIN (page_size, len - filled_nr); g_assert (need_nr >=0); if (need_nr == 0) return FALSE; String word; for (guint i = filled_nr; i < filled_nr + need_nr; i++) { if (i >= len) /* no more candidates */ break; lookup_candidate_t * candidate = NULL; pinyin_get_candidate (m_instance, i, &candidate); const gchar * phrase_string = NULL; pinyin_get_candidate_string (m_instance, candidate, &phrase_string); /* show get candidates. */ if (G_LIKELY (m_props.modeSimp ())) { word = phrase_string; } else { /* Traditional Chinese */ word.truncate (0); SimpTradConverter::simpToTrad (phrase_string, word); } Text text (word); m_lookup_table.appendCandidate (text); } return TRUE; } #endif gboolean PhoneticEditor::fillLookupTable (void) { guint len = 0; pinyin_get_n_candidate (m_instance, &len); String word; for (guint i = 0; i < len; i++) { lookup_candidate_t * candidate = NULL; pinyin_get_candidate (m_instance, i, &candidate); const gchar * phrase_string = NULL; pinyin_get_candidate_string (m_instance, candidate, &phrase_string); /* show get candidates. */ if (G_LIKELY (m_props.modeSimp ())) { word = phrase_string; } else { /* Traditional Chinese */ word.truncate (0); SimpTradConverter::simpToTrad (phrase_string, word); } Text text (word); /* show user candidate as blue. */ if (pinyin_is_user_candidate (m_instance, candidate)) text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1); m_lookup_table.appendCandidate (text); } return TRUE; } void PhoneticEditor::pageUp (void) { if (G_LIKELY (m_lookup_table.pageUp ())) { updateLookupTableFast (); updatePreeditText (); updateAuxiliaryText (); } } void PhoneticEditor::pageDown (void) { if (G_LIKELY(m_lookup_table.pageDown ())) { updateLookupTableFast (); updatePreeditText (); updateAuxiliaryText (); } } void PhoneticEditor::cursorUp (void) { if (G_LIKELY (m_lookup_table.cursorUp ())) { updateLookupTableFast (); updatePreeditText (); updateAuxiliaryText (); } } void PhoneticEditor::cursorDown (void) { if (G_LIKELY (m_lookup_table.cursorDown ())) { updateLookupTableFast (); updatePreeditText (); updateAuxiliaryText (); } } void PhoneticEditor::candidateClicked (guint index, guint button, guint state) { selectCandidateInPage (index); } void PhoneticEditor::reset (void) { m_pinyin_len = 0; m_lookup_table.clear (); pinyin_reset (m_instance); Editor::reset (); } void PhoneticEditor::update (void) { guint lookup_cursor = getLookupCursor (); pinyin_guess_candidates (m_instance, lookup_cursor, m_config.sortOption ()); updateLookupTable (); updatePreeditText (); updateAuxiliaryText (); } void PhoneticEditor::commit (const gchar *str) { StaticText text(str); commitText (text); } guint PhoneticEditor::getPinyinCursor () { /* Translate cursor position to pinyin position. */ size_t pinyin_cursor = 0; pinyin_get_pinyin_offset (m_instance, m_cursor, &pinyin_cursor); return pinyin_cursor; } guint PhoneticEditor::getLookupCursor (void) { guint lookup_cursor = getPinyinCursor (); /* show candidates when pinyin cursor is at end. */ if (lookup_cursor == m_text.length ()) lookup_cursor = 0; return lookup_cursor; } gboolean PhoneticEditor::selectCandidate (guint i) { guint len = 0; pinyin_get_n_candidate (m_instance, &len); if (G_UNLIKELY (i >= len)) return FALSE; guint lookup_cursor = getLookupCursor (); lookup_candidate_t * candidate = NULL; pinyin_get_candidate (m_instance, i, &candidate); lookup_candidate_type_t type; pinyin_get_candidate_type (m_instance, candidate, &type); if (NBEST_MATCH_CANDIDATE == type) { /* as nbest match candidate starts from the beginning of user input. */ pinyin_choose_candidate (m_instance, 0, candidate); guint8 index = 0; pinyin_get_candidate_nbest_index(m_instance, candidate, &index); commit (index); return TRUE; } lookup_cursor = pinyin_choose_candidate (m_instance, lookup_cursor, candidate); pinyin_guess_sentence (m_instance); if (lookup_cursor == m_text.length ()) { commit (); return TRUE; } PinyinKeyPos *pos = NULL; pinyin_get_pinyin_key_rest (m_instance, lookup_cursor, &pos); guint16 begin = 0; pinyin_get_pinyin_key_rest_positions (m_instance, pos, &begin, NULL); m_cursor = begin; update (); return TRUE; } gboolean PhoneticEditor::selectCandidateInPage (guint i) { guint page_size = m_lookup_table.pageSize (); guint cursor_pos = m_lookup_table.cursorPos (); if (G_UNLIKELY (i >= page_size)) return FALSE; i += (cursor_pos / page_size) * page_size; return selectCandidate (i); } gboolean PhoneticEditor::removeCharBefore (void) { if (G_UNLIKELY (m_cursor == 0)) return FALSE; m_cursor --; m_text.erase (m_cursor, 1); updatePinyin (); update (); return TRUE; } gboolean PhoneticEditor::removeCharAfter (void) { if (G_UNLIKELY (m_cursor == m_text.length ())) return FALSE; m_text.erase (m_cursor, 1); updatePinyin (); update (); return TRUE; } gboolean PhoneticEditor::moveCursorLeft (void) { if (G_UNLIKELY (m_cursor == 0)) return FALSE; m_cursor --; update (); return TRUE; } gboolean PhoneticEditor::moveCursorRight (void) { if (G_UNLIKELY (m_cursor == m_text.length ())) return FALSE; m_cursor ++; update (); return TRUE; } gboolean PhoneticEditor::moveCursorToBegin (void) { if (G_UNLIKELY (m_cursor == 0)) return FALSE; m_cursor = 0; update (); return TRUE; } gboolean PhoneticEditor::moveCursorToEnd (void) { if (G_UNLIKELY (m_cursor == m_text.length ())) return FALSE; m_cursor = m_text.length (); update (); return TRUE; } /* move cursor by word functions */ guint PhoneticEditor::getCursorLeftByWord (void) { size_t offset = 0; pinyin_get_pinyin_offset (m_instance, m_cursor, &offset); size_t cursor = 0; pinyin_get_left_pinyin_offset(m_instance, offset, &cursor); return cursor; } guint PhoneticEditor::getCursorRightByWord (void) { size_t offset = 0; pinyin_get_pinyin_offset (m_instance, m_cursor, &offset); size_t cursor = 0; pinyin_get_right_pinyin_offset(m_instance, offset, &cursor); return cursor; } gboolean PhoneticEditor::removeWordBefore (void) { if (G_UNLIKELY (m_cursor == 0)) return FALSE; guint cursor = getCursorLeftByWord (); m_text.erase (cursor, m_cursor - cursor); m_cursor = cursor; updatePinyin (); update (); return TRUE; } gboolean PhoneticEditor::removeWordAfter (void) { if (G_UNLIKELY (m_cursor == m_text.length ())) return FALSE; guint cursor = getCursorRightByWord (); m_text.erase (m_cursor, cursor - m_cursor); updatePinyin (); update (); return TRUE; } gboolean PhoneticEditor::moveCursorLeftByWord (void) { if (G_UNLIKELY (m_cursor == 0)) return FALSE; guint cursor = getCursorLeftByWord (); m_cursor = cursor; update (); return TRUE; } gboolean PhoneticEditor::moveCursorRightByWord (void) { if (G_UNLIKELY (m_cursor == m_text.length ())) return FALSE; guint cursor = getCursorRightByWord (); m_cursor = cursor; update (); return TRUE; }