diff options
Diffstat (limited to 'src/BopomofoContext.cc')
-rw-r--r-- | src/BopomofoContext.cc | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/src/BopomofoContext.cc b/src/BopomofoContext.cc new file mode 100644 index 0000000..0b8ff34 --- /dev/null +++ b/src/BopomofoContext.cc @@ -0,0 +1,505 @@ +/* vim:set et ts=4 sts=4: + * + * libpyzy - The Chinese PinYin and Bopomofo conversion library. + * + * Copyright (c) 2008-2010 Peng Huang <shawn.p.huang@gmail.com> + * Copyright (c) 2010 BYVoid <byvoid1@gmail.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +#include "BopomofoContext.h" + +#include "Config.h" +#include "PinyinParser.h" +#include "SimpTradConverter.h" + +namespace PyZy { +#include "BopomofoKeyboard.h" + +const static char * bopomofo_select_keys[] = { + "1234567890", + "asdfghjkl;", + "1qaz2wsxed", + "asdfzxcvgb", + "1234qweras", + "aoeu;qjkix", + "aoeuhtnsid", + "aoeuidhtns", + "qweasdzxcr" +}; + +BopomofoContext::BopomofoContext (PhoneticContext::Observer *observer) + : PhoneticContext (observer), + m_bopomofo_schema (BOPOMOFO_KEYBOARD_STANDARD) +{ +} + +BopomofoContext::~BopomofoContext (void) +{ +} + +bool +BopomofoContext::insert (char ch) +{ + if (keyvalToBopomofo (ch) == BOPOMOFO_ZERO) { + return false; + } + + /* is full */ + if (G_UNLIKELY (m_text.length () >= MAX_PINYIN_LEN)) + return true; + + m_text.insert (m_cursor++, ch); + updateInputText (); + updateCursor (); + + if (G_UNLIKELY (!(m_config.option & PINYIN_INCOMPLETE_PINYIN))) { + updateSpecialPhrases (); + updatePinyin (); + } + else if (G_LIKELY (m_cursor <= m_pinyin_len + 2)) { + updateSpecialPhrases (); + updatePinyin (); + } + else { + if (updateSpecialPhrases ()) { + update (); + } + else { + updatePreeditText (); + updateAuxiliaryText (); + } + } + + return true; +} + +bool +BopomofoContext::removeCharBefore (void) +{ + if (G_UNLIKELY (m_cursor == 0)) + return false; + + m_cursor --; + m_text.erase (m_cursor, 1); + updateInputText (); + updateCursor (); + updateSpecialPhrases (); + updatePinyin (); + + return true; +} + +bool +BopomofoContext::removeCharAfter (void) +{ + if (G_UNLIKELY (m_cursor == m_text.length ())) + return false; + + m_text.erase (m_cursor, 1); + updateInputText (); + updatePreeditText (); + updateAuxiliaryText (); + + return true; +} + +bool +BopomofoContext::removeWordBefore (void) +{ + if (G_UNLIKELY (m_cursor == 0)) + return false; + + size_t cursor; + + if (G_UNLIKELY (m_cursor > m_pinyin_len)) { + cursor = m_pinyin_len; + } + else { + const Pinyin & p = *m_pinyin.back (); + cursor = m_cursor - p.len; + m_pinyin_len -= p.len; + m_pinyin.pop_back (); + } + + m_text.erase (cursor, m_cursor - cursor); + m_cursor = cursor; + updateInputText (); + updateCursor (); + updateSpecialPhrases (); + updatePhraseEditor (); + update (); + return true; +} + +bool +BopomofoContext::removeWordAfter (void) +{ + if (G_UNLIKELY (m_cursor == m_text.length ())) + return false; + + m_text.erase (m_cursor, -1); + updateInputText (); + updatePreeditText (); + updateAuxiliaryText (); + return true; +} + +bool +BopomofoContext::moveCursorLeft (void) +{ + if (G_UNLIKELY (m_cursor == 0)) + return false; + + m_cursor --; + updateCursor (); + updateSpecialPhrases (); + updatePinyin (); + + return true; +} + +bool +BopomofoContext::moveCursorRight (void) +{ + if (G_UNLIKELY (m_cursor == m_text.length ())) + return false; + + m_cursor ++; + updateCursor (); + updateSpecialPhrases (); + updatePinyin (); + + return true; +} + +bool +BopomofoContext::moveCursorLeftByWord (void) +{ + if (G_UNLIKELY (m_cursor == 0)) + return false; + + if (G_UNLIKELY (m_cursor > m_pinyin_len)) { + m_cursor = m_pinyin_len; + return true; + } + + const Pinyin & p = *m_pinyin.back (); + m_cursor -= p.len; + m_pinyin_len -= p.len; + m_pinyin.pop_back (); + + updateCursor (); + updateSpecialPhrases (); + updatePhraseEditor (); + update (); + + return true; +} + +bool +BopomofoContext::moveCursorRightByWord (void) +{ + return moveCursorToEnd (); +} + +bool +BopomofoContext::moveCursorToBegin (void) +{ + if (G_UNLIKELY (m_cursor == 0)) + return false; + + m_cursor = 0; + m_pinyin.clear (); + m_pinyin_len = 0; + + updateCursor (); + updateSpecialPhrases (); + updatePhraseEditor (); + update (); + + return true; +} + +bool +BopomofoContext::moveCursorToEnd (void) +{ + if (G_UNLIKELY (m_cursor == m_text.length ())) + return false; + + m_cursor = m_text.length (); + updateCursor (); + updateSpecialPhrases (); + updatePinyin (); + + return true; +} + +void +BopomofoContext::updatePinyin (void) +{ + if (G_UNLIKELY (m_text.empty ())) { + m_pinyin.clear (); + m_pinyin_len = 0; + } + else { + std::wstring bopomofo; + for(String::iterator i = m_text.begin (); i != m_text.end (); ++i) { + bopomofo += bopomofo_char[keyvalToBopomofo (*i)]; + } + + m_pinyin_len = PinyinParser::parseBopomofo ( + bopomofo, // bopomofo + m_cursor, // text length + m_config.option, // option + m_pinyin, // result + MAX_PHRASE_LEN); // max result length + } + + updatePhraseEditor (); + update (); +} + +void +BopomofoContext::updateAuxiliaryText (void) +{ + if (G_UNLIKELY (m_text.empty () || !hasCandidate (0))) { + m_auxiliary_text = ""; + PhoneticContext::updateAuxiliaryText (); + return; + } + + m_buffer.clear (); + + if (m_selected_special_phrase.empty ()) { + size_t si = 0; + size_t m_text_len = m_text.length(); + for (size_t i = m_phrase_editor.cursor (); i < m_pinyin.size (); ++i) { + if (G_LIKELY (i != m_phrase_editor.cursor ())) + m_buffer << ','; + m_buffer << (unichar *)m_pinyin[i]->bopomofo; + for (size_t sj = 0; m_pinyin[i]->bopomofo[sj] == bopomofo_char[keyvalToBopomofo(m_text.c_str()[si])] ; si++,sj++); + + if (si < m_text_len) { + int ch = keyvalToBopomofo(m_text.c_str()[si]); + if (ch >= BOPOMOFO_TONE_2 && ch <= BOPOMOFO_TONE_5) { + m_buffer.appendUnichar(bopomofo_char[ch]); + ++si; + } + } + } + + for (String::iterator i = m_text.begin () + m_pinyin_len; i != m_text.end (); i++) { + if (m_cursor == (size_t)(i - m_text.begin ())) + m_buffer << '|'; + m_buffer.appendUnichar (bopomofo_char[keyvalToBopomofo (*i)]); + } + if (m_cursor == m_text.length ()) + m_buffer << '|'; + } + else { + if (m_cursor < m_text.size ()) { + m_buffer << '|' << textAfterCursor (); + } + } + + m_auxiliary_text = m_buffer; + PhoneticContext::updateAuxiliaryText (); +} + +void +BopomofoContext::commit (CommitType type) +{ + if (G_UNLIKELY (m_buffer.empty ())) + return; + + m_buffer.clear (); + + if (G_LIKELY (type == TYPE_CONVERTED)) { + m_buffer << m_phrase_editor.selectedString (); + + const char *p; + + if (m_selected_special_phrase.empty ()) { + p = textAfterPinyin (m_buffer.utf8Length ()); + } + else { + m_buffer << m_selected_special_phrase; + p = textAfterCursor (); + } + + while (*p != '\0') { + m_buffer.appendUnichar ((unichar)bopomofo_char[keyvalToBopomofo (*p++)]); + } + + m_phrase_editor.commit (); + } + else if (type == TYPE_PHONETIC) { + const char *p = m_text; + while (*p != '\0') { + m_buffer.appendUnichar ((unichar)bopomofo_char[keyvalToBopomofo (*p++)]); + } + } else { + m_buffer = m_text; + m_phrase_editor.reset (); + } + + resetContext (); + updateInputText (); + updateCursor (); + update (); + PhoneticContext::commitText (m_buffer); +} + +void +BopomofoContext::updatePreeditText (void) +{ + /* preedit text = selected phrases + highlight candidate + rest text */ + if (G_UNLIKELY (m_phrase_editor.empty () && m_text.empty ())) { + m_preedit_text.clear (); + PhoneticContext::updatePreeditText (); + return; + } + + size_t edit_begin_word = 0; + size_t edit_end_word = 0; + size_t edit_begin_byte = 0; + size_t edit_end_byte = 0; + + m_buffer.clear (); + m_preedit_text.clear (); + + /* add selected phrases */ + m_buffer << m_phrase_editor.selectedString (); + + if (G_UNLIKELY (! m_selected_special_phrase.empty ())) { + /* add selected special phrase */ + m_buffer << m_selected_special_phrase; + edit_begin_word = edit_end_word = m_buffer.utf8Length (); + edit_begin_byte = edit_end_byte = m_buffer.size (); + + /* append text after cursor */ + m_buffer << textAfterCursor (); + } + else { + edit_begin_word = m_buffer.utf8Length (); + edit_begin_byte = m_buffer.size (); + + if (hasCandidate (0)) { + size_t index = m_focused_candidate; + + if (index < m_special_phrases.size ()) { + m_buffer << m_special_phrases[index].c_str (); + edit_end_word = m_buffer.utf8Length (); + edit_end_byte = m_buffer.size (); + + /* append text after cursor */ + m_buffer << textAfterCursor (); + } + else { + const Phrase & candidate = m_phrase_editor.candidate (index - m_special_phrases.size ()); + if (m_text.size () == m_cursor) { + /* cursor at end */ + if (m_config.modeSimp) + m_buffer << candidate; + else + SimpTradConverter::simpToTrad (candidate, m_buffer); + edit_end_word = m_buffer.utf8Length (); + edit_end_byte = m_buffer.size (); + /* append rest text */ + for (const char *p=m_text.c_str() + m_pinyin_len; *p ;++p) { + m_buffer.appendUnichar(bopomofo_char[keyvalToBopomofo(*p)]); + } + } + else { + for (const char *p = m_text.c_str (); *p; ++p) { + if ((size_t) (p - m_text.c_str ()) == m_cursor) + m_buffer << ' '; + m_buffer.appendUnichar (bopomofo_char[keyvalToBopomofo (*p)]); + } + edit_end_word = m_buffer.utf8Length (); + edit_end_byte = m_buffer.size (); + } + } + } + else { + edit_end_word = m_buffer.utf8Length (); + edit_end_byte = m_buffer.size (); + for (const char *p=m_text.c_str () + m_pinyin_len; *p ; ++p) { + m_buffer.appendUnichar (bopomofo_char[keyvalToBopomofo (*p)]); + } + } + } + + m_preedit_text.selected_text = m_buffer.substr (0, edit_begin_byte); + m_preedit_text.candidate_text = m_buffer.substr (edit_begin_byte, edit_end_byte - edit_begin_byte); + m_preedit_text.rest_text = m_buffer.substr (edit_end_byte); + + PhoneticContext::updatePreeditText (); +} + +Variant +BopomofoContext::getProperty (PropertyName name) const +{ + if (name == PROPERTY_BOPOMOFO_SCHEMA) { + return Variant::fromUnsignedInt(m_bopomofo_schema); + } + return PhoneticContext::getProperty (name); +} + +bool +BopomofoContext::setProperty (PropertyName name, const Variant &variant) +{ + if (name == PROPERTY_BOPOMOFO_SCHEMA) { + if (variant.getType () != Variant::TYPE_UNSIGNED_INT) { + return false; + } + const unsigned int schema = variant.getUnsignedInt (); + if (schema >= BOPOMOFO_KEYBOARD_LAST) { + return false; + } + + m_bopomofo_schema = schema; + return true; + } + + return PhoneticContext::setProperty (name, variant); +} + +static int +keyboard_cmp (const void * p1, const void * p2) +{ + const int s1 = GPOINTER_TO_INT (p1); + const unsigned char *s2 = (const unsigned char *) p2; + return s1 - s2[0]; +} + +int +BopomofoContext::keyvalToBopomofo(int ch) +{ + const unsigned int keyboard = m_bopomofo_schema; + const unsigned char *brs; + brs = (const unsigned char *) std::bsearch (GINT_TO_POINTER (ch), + bopomofo_keyboard[keyboard], + G_N_ELEMENTS (bopomofo_keyboard[keyboard]), + sizeof(bopomofo_keyboard[keyboard][0]), + keyboard_cmp); + if (G_UNLIKELY (brs == NULL)) + return BOPOMOFO_ZERO; + return brs[1]; +} + +}; // namespace PyZy |