Skip to content
PyZyDoublePinyinContext.cc 10.5 KiB
Newer Older
/* 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>
 *
 * 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 "PyZyDoublePinyinContext.h"
#include "PyZyConfig.h"
#include "PyZyPinyinParser.h"

namespace PyZy {

#define DEFINE_DOUBLE_PINYIN_TABLES
#include "PyZyDoublePinyinTable.h"

/*
 * c in 'a' ... 'z' => id = c - 'a'
 * c == ';'         => id = 26
 * else             => id = -1
 */
#define ID(c) \
    ((c >= 'a' && c <= 'z') ? c - 'a' : (c == ';' ? 26 : -1))

#define ID_TO_SHENG(id) \
    (double_pinyin_map[m_config.doublePinyinSchema ()].sheng[id])
#define ID_TO_YUNS(id) \
    (double_pinyin_map[m_config.doublePinyinSchema ()].yun[id])

#define IS_ALPHA(c) \
    ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))

DoublePinyinContext::DoublePinyinContext (Config & config, PhoneticContext::Observer *observer)
    : PinyinContext (config, observer)
{
}

Hiroshi Sumita's avatar
Hiroshi Sumita committed
DoublePinyinContext::~DoublePinyinContext ()
{
}

bool
DoublePinyinContext::insert (char ch)
    const int id = ID (ch);

    if (id == -1) {
        /* it is not available ch */
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;
    }

    if (G_UNLIKELY (m_text.empty () &&
        ID_TO_SHENG (id) == PINYIN_ID_VOID)) {
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;
    /* is full */
    if (G_UNLIKELY (m_text.length () >= MAX_PINYIN_LEN))
        return true;

    m_text.insert (m_cursor++, ch);

Hiroshi Sumita's avatar
Hiroshi Sumita committed
    if (m_cursor > m_pinyin_len + 2 || updatePinyin (false) == false) {
        if (!IS_ALPHA (ch)) {
            m_text.erase (--m_cursor, 1);
Hiroshi Sumita's avatar
Hiroshi Sumita committed
            return false;
        }
        else {
            if (updateSpecialPhrases ()) {
                update ();
            }
            else {
                updatePreeditText ();
                updateAuxiliaryText ();
            }
Hiroshi Sumita's avatar
Hiroshi Sumita committed
            return true;
        }
    }
    else {
        updateSpecialPhrases ();
        updatePhraseEditor ();
        update ();
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::removeCharBefore (void)
{
    if (G_UNLIKELY (m_cursor == 0))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    m_cursor --;
    m_text.erase (m_cursor, 1);

Hiroshi Sumita's avatar
Hiroshi Sumita committed
    if (updatePinyin (false)) {
        updateSpecialPhrases ();
        updatePhraseEditor ();
        update ();
    }
    else {
        if (updateSpecialPhrases ()) {
            update ();
        }
        else {
            updatePreeditText ();
            updateAuxiliaryText ();
        }
    }
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::removeCharAfter (void)
{
    if (G_UNLIKELY (m_cursor == m_text.length ()))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    m_text.erase (m_cursor, 1);
    if (updateSpecialPhrases ()) {
        update ();
    }
    else {
        updatePreeditText ();
        updateAuxiliaryText ();
    }
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::removeWordBefore (void)
{
    if (G_UNLIKELY (m_cursor == 0))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    if (G_UNLIKELY (m_cursor > m_pinyin_len)) {
        m_text.erase (m_pinyin_len, m_cursor - m_pinyin_len);
        m_cursor = m_pinyin_len;
        if (updateSpecialPhrases ()) {
            update ();
        }
        else {
            updatePreeditText ();
            updateAuxiliaryText ();
        }
    }
    else {
        m_pinyin_len = m_pinyin.back ().begin;
        m_pinyin.pop_back ();
        m_text.erase (m_pinyin_len, m_cursor - m_pinyin_len);
        m_cursor = m_pinyin_len;
        updateSpecialPhrases ();
        updatePhraseEditor ();
        update ();
    }

Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::removeWordAfter (void)
{
    if (G_UNLIKELY (m_cursor == m_text.length ()))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    m_text.erase (m_cursor);
    if (updateSpecialPhrases ()) {
        update ();
    }
    else {
        updatePreeditText ();
        updateAuxiliaryText ();
    }
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::moveCursorLeft (void)
{
    if (G_UNLIKELY (m_cursor == 0))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    m_cursor --;

    if (m_cursor >= m_pinyin_len) {
        if (updateSpecialPhrases ()) {
            update ();
        }
        else {
            updatePreeditText ();
            updateAuxiliaryText ();
        }
    }
    else {
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        if (updatePinyin (false)) {
            updateSpecialPhrases ();
            updatePhraseEditor ();
            update ();
        }
        else {
            if (updateSpecialPhrases ()) {
                update ();
            }
            else {
                updatePreeditText ();
                updateAuxiliaryText ();
            }
        }
    }

Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::moveCursorRight (void)
{
    if (G_UNLIKELY (m_cursor == m_text.length ()))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    if (updatePinyin (false)) {
        updateSpecialPhrases ();
        updatePhraseEditor ();
        update ();
    }
    else {
        if (updateSpecialPhrases ()) {
            update ();
        }
        else {
            updatePreeditText ();
            updateAuxiliaryText ();
        }
    }
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::moveCursorLeftByWord (void)
{
    if (G_UNLIKELY (m_cursor == 0))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    if (G_UNLIKELY (m_cursor > m_pinyin_len)) {
        m_cursor = m_pinyin_len;
        if (updateSpecialPhrases ()) {
            update ();
        }
        else {
            updatePreeditText ();
            updateAuxiliaryText ();
        }
    }
    else {
        m_cursor = m_pinyin_len = m_pinyin.back ().begin;
        m_pinyin.pop_back ();
        updateSpecialPhrases ();
        updatePhraseEditor ();
        update ();
    }

Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::moveCursorRightByWord (void)
{
    return moveCursorToEnd ();
}

Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::moveCursorToBegin (void)
{
    if (G_UNLIKELY (m_cursor == 0))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    m_cursor = 0;
    m_pinyin.clear ();
    m_pinyin_len = 0;
    updateSpecialPhrases ();
    updatePhraseEditor ();
    update ();

Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
bool
DoublePinyinContext::moveCursorToEnd (void)
{
    if (G_UNLIKELY (m_cursor == m_text.length ()))
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return false;

    m_cursor = m_text.length ();
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    if (updatePinyin (false)) {
        updateSpecialPhrases ();
        updatePhraseEditor ();
        update ();
    }
    else {
        if (updateSpecialPhrases ()) {
            update ();
        }
        else {
            updatePreeditText ();
            updateAuxiliaryText ();
        }
    }
Hiroshi Sumita's avatar
Hiroshi Sumita committed
    return true;
}

inline const Pinyin *
DoublePinyinContext::isPinyin (int i)
{
    if ((m_config.option () & PINYIN_INCOMPLETE_PINYIN) == 0) {
        return NULL;
    }

    char sheng = ID_TO_SHENG (i);

    if (sheng == PINYIN_ID_VOID) {
        return NULL;
    }

    return PinyinParser::isPinyin (sheng, 0, PINYIN_INCOMPLETE_PINYIN);
}

inline const Pinyin *
DoublePinyinContext::isPinyin (int i, int j)
{
    const Pinyin *pinyin;
    char sheng = ID_TO_SHENG (i);
    const char *yun = ID_TO_YUNS (j);

    if (sheng == PINYIN_ID_VOID || yun[0] == PINYIN_ID_VOID)
        return NULL;

    if (sheng == PINYIN_ID_ZERO && yun[0] == PINYIN_ID_ZERO)
        return NULL;

    if (yun[1] == PINYIN_ID_VOID) {
        return PinyinParser::isPinyin (sheng, yun[0],
                        m_config.option () & (PINYIN_FUZZY_ALL | PINYIN_CORRECT_V_TO_U));
    }

    pinyin = PinyinParser::isPinyin (sheng, yun[0],
                    m_config.option () & (PINYIN_FUZZY_ALL));
    if (pinyin == NULL)
        pinyin = PinyinParser::isPinyin (sheng, yun[1],
                        m_config.option () & (PINYIN_FUZZY_ALL));
    if (pinyin != NULL)
        return pinyin;

    /* if sheng == j q x y and yun == v, try to correct v to u */
    if ((m_config.option () & PINYIN_CORRECT_V_TO_U) == 0)
        return NULL;

    if (yun[0] != PINYIN_ID_V && yun[1] != PINYIN_ID_V)
        return NULL;

    switch (sheng) {
    case PINYIN_ID_J:
    case PINYIN_ID_Q:
    case PINYIN_ID_X:
    case PINYIN_ID_Y:
        return PinyinParser::isPinyin (sheng, PINYIN_ID_V,
                            m_config.option () & (PINYIN_FUZZY_ALL | PINYIN_CORRECT_V_TO_U));
    default:
        return NULL;
    }
}

Hiroshi Sumita's avatar
Hiroshi Sumita committed
inline bool
DoublePinyinContext::updatePinyin (bool all)
    bool retval = false;

    if (all &&
        (m_pinyin_len != 0 || !m_pinyin.empty ())) {
        m_pinyin.clear ();
        m_pinyin_len = 0;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        retval = true;
    }

    if (m_pinyin_len > m_cursor) {
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        retval = true;
        while (m_pinyin_len > m_cursor) {
            m_pinyin_len = m_pinyin.back ().begin;
            m_pinyin.pop_back ();
        }
    }

    if (m_pinyin_len == m_cursor) {
        return retval;
    }

    if (m_pinyin_len < m_cursor) {
        size_t len = m_pinyin_len;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        if (m_pinyin.empty () == false &&
            m_pinyin.back ()->flags & PINYIN_INCOMPLETE_PINYIN) {
            const Pinyin *pinyin = isPinyin (ID (m_text[m_pinyin_len -1]),ID (m_text[m_pinyin_len]));
            if (pinyin) {
                m_pinyin.pop_back ();
                m_pinyin.append (pinyin, m_pinyin_len - 1, 2);
                m_pinyin_len += 1;
            }
        }
        while (m_pinyin_len < m_cursor && m_pinyin.size () < MAX_PHRASE_LEN) {
            const Pinyin *pinyin = NULL;
            if (m_pinyin_len == m_cursor - 1) {
                pinyin = isPinyin (ID (m_text[m_pinyin_len]));
            }
            else {
                pinyin = isPinyin (ID (m_text[m_pinyin_len]), ID (m_text[m_pinyin_len + 1]));
                if (pinyin == NULL)
                    pinyin = isPinyin (ID (m_text[m_pinyin_len]));
            }
            if (pinyin == NULL)
                break;
            if (pinyin->flags & PINYIN_INCOMPLETE_PINYIN) {
                m_pinyin.append (pinyin, m_pinyin_len, 1);
                m_pinyin_len += 1;
            }
            else {
                m_pinyin.append (pinyin, m_pinyin_len, 2);
                m_pinyin_len += 2;
            }
        }
        if (len == m_pinyin_len)
            return retval;
Hiroshi Sumita's avatar
Hiroshi Sumita committed
        return true;
    }
    return retval;
}

};  // namespace PyZy