Newer
Older
scim-wnn / honoka / src / honoka_imengine.cpp
/***************************************************************************
 *   Copyright (C) 2004 by TAM(Teppei Tamra)                               *
 *   tam-t@par.odn.ne.jp                                                   *
 *                                                                         *
 *   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 of the License, 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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
  #include <config.h>
#endif

#include <sys/types.h>
#include <dirent.h>
#include <set>
 
// 国際化のおまじない。
#ifdef HAVE_GETTEXT
  #include <libintl.h>
  #define _(String) dgettext(GETTEXT_PACKAGE,String)
  #define N_(String) (String)
#else
  #define _(String) (String)
  #define N_(String) (String)
  #define bindtextdomain(Package,Directory)
  #define textdomain(domain)
  #define bind_textdomain_codeset(domain,codeset)
#endif

// scimのおまじない。
#include "honoka_imengine.h"
#include "honoka_def.h"

#define scim_module_init honoka_LTX_scim_module_init
#define scim_module_exit honoka_LTX_scim_module_exit
#define scim_imengine_module_init honoka_LTX_scim_imengine_module_init
#define scim_imengine_module_create_factory honoka_LTX_scim_imengine_module_create_factory
#ifndef HONOKA_ICON_FILE
  #define HONOKA_ICON_FILE           (SCIM_ICONDIR "/honoka.png")
#endif


#define DEFAULT 0
#define IKEIJI  1
#define RENSOU  2
#define YOSOKU  3


static Pointer <HonokaFactory> _honoka_factory;
static ConfigPointer _scim_config;


extern "C" {
    void scim_module_init (void)
    {
        bindtextdomain (GETTEXT_PACKAGE, HONOKA_LOCALEDIR);
        bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    }

    void scim_module_exit (void)
    {
        _honoka_factory.reset ();
        _scim_config.reset ();
    }

    unsigned int scim_imengine_module_init (const ConfigPointer &config)
    {
        _scim_config = config;
        return 1;
    }

    IMEngineFactoryPointer scim_imengine_module_create_factory (unsigned int factory)
    {
        if (factory != 0) return NULL;
        if (_honoka_factory.null ()) {
            _honoka_factory =
                new HonokaFactory (utf8_mbstowcs (String (_("Honoka"))),String("ja_JP"));
        }
        return _honoka_factory;
    }
}

HonokaFactory::HonokaFactory() {
    m_name = utf8_mbstowcs(_("Honoka"));
    set_languages(String("ja_JP"));
}

HonokaFactory::~ HonokaFactory() {
}

HonokaFactory::HonokaFactory(const WideString & name, const String & languages) {
    if (name.length () <= 8)
        m_name = name;
    else
        m_name.assign (name.begin (), name.begin () + 8);
    if (languages == String ("default"))
        set_languages (String (_("ja_JP")));
    else
        set_languages (languages);
}

WideString HonokaFactory::get_name () const
{
    return m_name;
}

WideString HonokaFactory::get_authors () const
{
    return utf8_mbstowcs (String (_("(C) 2004 TAM(Teppei Tamra) <tam-t@par.odn.ne.jp>")));
}

WideString HonokaFactory::get_credits () const
{
    return WideString ();
}

WideString HonokaFactory::get_help () const
{
    return utf8_mbstowcs (String (_("SCIM-WNN HELP")));
}

String HonokaFactory::get_uuid () const
{
    // return String ("c069395a-d62f-4a77-8229-446e44a99976");
    return String ("8bb03c1c-db6c-41b1-91bd-b7fb7dd70343");
}

String HonokaFactory::get_icon_file () const
{
    return String (HONOKA_ICON_FILE);
}

IMEngineInstancePointer HonokaFactory::create_instance (const String& encoding, int id)
{
    return new HonokaInstance (this, encoding, id);
}





// 現物。


HonokaInstance::HonokaInstance (HonokaFactory *factory, const String& encoding, int id)
    : IMEngineInstanceBase (factory, encoding, id)
{
    m_iconv.set_encoding ("EUC-JP");
    
    DIR *dir = opendir(HONOKA_PLUGINDIR);
    if (dir) {
        struct dirent *entry;
        //
        // プラグイン読み込み部分。
        //
        set<String> pluginNameList;
        while((entry = readdir(dir)) != NULL) {
            // まず名前を決定します。
            String d = entry->d_name;
            if ((d.substr(0,6) == "plugin") && (d.substr(d.length() - 3,3) == ".so")) {
                if (!_scim_config->read(String(HONOKA_CONFIG_PLUGINLOADER_PREFIX) + "/" + d.substr(0,d.length() - 3),true))
                    continue;
                d = String(HONOKA_PLUGINDIR) + "/" + d;
                // 実際にロードします。
                void* plugin = dlopen(d.c_str(), RTLD_LAZY);
                if (!plugin) continue;
                // 関数を抜き取ります。
                createInstanceFunc *getInstance = (createInstanceFunc *)dlsym(plugin,"getHonokaPluginInstance");
                deleteInstanceFunc *deleteInstance = (deleteInstanceFunc *)dlsym(plugin,"deleteHonokaPluginInstance");
                getPluginVersionFunc *getPluginVersion = (getPluginVersionFunc *)dlsym(plugin,"getHonokaPluginVersion");
                // 関数が不足した場合はアンロードします。
                if ((!getInstance) || (!deleteInstance) || (!getPluginVersion)) {
                    dlclose(plugin);
                    continue;
                }
                // バージョンチェックします。
                if (getPluginVersion() == HONOKA_PLUGIN_VERSION) {
                    // いきなりインスタンスをゲトします。
                    HonokaPluginBase *p = getInstance(_scim_config);
                    // プラグイン情報を得ておきます。
                    HonokaPluginEntry pe;
                    pe.filename = d;
                    pe.createInstance = getInstance;
                    pe.deleteInstance = deleteInstance;
                    pe.getPluginVersion = getPluginVersion;
                    pe.instance = p;
                    pe.name = p->getName();
                    pe.dll = plugin;
                    // 同名のプラグインがあった場合はロードを中止します。
                    //if (plugins.find(p->getName()) != plugins.end()) {
                    if (pluginNameList.find(p->getName()) != pluginNameList.end()) {
                        deleteInstance(p);
                        dlclose(plugin);
                    } else
                    if (p->getPluginType() == "Multiple") {
                        plugins.push_back(pe);
                        pluginNameList.insert(p->getName());
                        for(unsigned int i = 0;i < static_cast<HonokaMultiplePluginBase *>(p)->getPluginCount();i++)
                            pluginCheck(static_cast<HonokaMultiplePluginBase *>(p)->getPluginInstanceAt(i));
                    } else
                    if (pluginCheck(p)) {
                        plugins.push_back(pe);
                        pluginNameList.insert(p->getName());
                    } else {
                        deleteInstance(p);
                        dlclose(plugin);
                    }
                } else {
                    dlclose(plugin);
                }
            }
        }
    }
    // プラグインがなければデフォの低機能なベースクラスを使います。
    if (!convertors.size()) convertors.push_back(new Convertor(_scim_config));
    if (!preeditors.size()) preeditors.push_back(new PreEditor(_scim_config));
    if (!predictors.size()) predictors.push_back(new Predictor(_scim_config));
    
    // 初期利用Convertor/PreEditorを指定します。
    m_convertor = convertors.at(0);
    m_preeditor = preeditors.at(0);
    m_predictor = predictors.at(0);
    
    m_multi = new MultiConvertor(_scim_config,this);
    
    // 初期化へ。
    init();
}
bool HonokaInstance::pluginCheck(HonokaPluginBase *p) {

    // Convertorの場合。
    if (p->getPluginType() == "Convertor") {
        convertors.push_back(static_cast<Convertor *>(p));
        return true;
    } else
    // PreEditorの場合。
    if (p->getPluginType() == "PreEditor") {
        preeditors.push_back(static_cast<PreEditor *>(p));
        return true;
    } else
    // Predictorの場合。
    if (p->getPluginType() == "Predictor") {
        predictors.push_back(static_cast<Predictor *>(p));
        return true;
    }
    return false;
}

HonokaInstance::~HonokaInstance()
{
/*    for(unsigned int i = 0;i < preeditors.size();i ++) {
        delete(preeditors.at(i));
    }
    if (m_convertor->isConnected()) m_convertor->disconnect();
*/
    for(unsigned int i = 0;i < plugins.size();i ++) {
        plugins[i].deleteInstance(plugins[i].instance);
        dlclose(plugins[i].dll);
    }
}


/*!
    \fn HonokaInstance::init()
 */
void HonokaInstance::init()
{
    // 初期化。

    m_conversion = false;
    m_yosoku = false;
    m_lookup = false;
    
    while(preeditStack.size()) {
        preeditStack.pop();
    }

    // @todo if connected to jserver, should disconnect this.

    alp = _scim_config->read(String(HONOKA_CONFIG_ALP),HONOKA_DEFAULT_ALP);
    mini_status = _scim_config->read(String(HONOKA_CONFIG_MINISTATUS),HONOKA_DEFAULT_MINISTATUS);
    numkeyselect = _scim_config->read(String(HONOKA_CONFIG_NUMKEY_SELECT),HONOKA_DEFAULT_NUMKEY_SELECT);
    yosoku = _scim_config->read(String(HONOKA_CONFIG_YOSOKU),HONOKA_DEFAULT_YOSOKU);
    sType = _scim_config->read(String(HONOKA_CONFIG_SERVERTYPE),String(HONOKA_DEFAULT_SERVERTYPE));
    
    
    defaultPreEditor = _scim_config->read(String(HONOKA_CONFIG_DEFAULT_PREEDITOR),String(HONOKA_DEFAULT_DEFAULT_PREEDITOR));
    defaultConvertor = _scim_config->read(String(HONOKA_CONFIG_DEFAULT_CONVERTOR),String(HONOKA_DEFAULT_DEFAULT_CONVERTOR));
    defaultPredictor = _scim_config->read(String(HONOKA_CONFIG_DEFAULT_PREDICTOR),String(HONOKA_DEFAULT_DEFAULT_PREDICTOR));
    auto_conversion = _scim_config->read(String(HONOKA_CONFIG_AUTO_CONVERSION),HONOKA_DEFAULT_AUTO_CONVERSION);;

    // デフォルトキー設定。
    scim_string_to_key_list(k_conversion_start,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_START),
            String(HONOKA_DEFAULT_KEY_CONVERSION_START)));
    scim_string_to_key_list(k_cancel,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CANCEL),
            String(HONOKA_DEFAULT_KEY_CANCEL)));
    scim_string_to_key_list(k_delete,
        _scim_config->read(String(HONOKA_CONFIG_KEY_DELETE),
            String(HONOKA_DEFAULT_KEY_DELETE)));
    scim_string_to_key_list(k_backspace,
        _scim_config->read(String(HONOKA_CONFIG_KEY_BACKSPACE),
            String(HONOKA_DEFAULT_KEY_BACKSPACE)));
    scim_string_to_key_list(k_commit,
        _scim_config->read(String(HONOKA_CONFIG_KEY_COMMIT),
            String(HONOKA_DEFAULT_KEY_COMMIT)));
    scim_string_to_key_list(k_conversion_next,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_NEXT),
            String(HONOKA_DEFAULT_KEY_CONVERSION_NEXT)));
    scim_string_to_key_list(k_conversion_prev,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_PREV),
            String(HONOKA_DEFAULT_KEY_CONVERSION_PREV)));
    scim_string_to_key_list(k_conversion_expand,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_EXPAND),
            String(HONOKA_DEFAULT_KEY_CONVERSION_EXPAND)));
    scim_string_to_key_list(k_conversion_shrink,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_SHRINK),
            String(HONOKA_DEFAULT_KEY_CONVERSION_SHRINK)));
    scim_string_to_key_list(k_conversion_forward,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_FORWARD),
            String(HONOKA_DEFAULT_KEY_CONVERSION_FORWARD)));
    scim_string_to_key_list(k_conversion_backward,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_BACKWARD),
            String(HONOKA_DEFAULT_KEY_CONVERSION_BACKWARD)));
    scim_string_to_key_list(k_forward,
        _scim_config->read(String(HONOKA_CONFIG_KEY_FORWARD),
            String(HONOKA_DEFAULT_KEY_FORWARD)));
    scim_string_to_key_list(k_backward,
        _scim_config->read(String(HONOKA_CONFIG_KEY_BACKWARD),
            String(HONOKA_DEFAULT_KEY_BACKWARD)));
    scim_string_to_key_list(k_home,
        _scim_config->read(String(HONOKA_CONFIG_KEY_HOME),
            String(HONOKA_DEFAULT_KEY_HOME)));
    scim_string_to_key_list(k_end,
        _scim_config->read(String(HONOKA_CONFIG_KEY_END),
            String(HONOKA_DEFAULT_KEY_END)));
    scim_string_to_key_list(k_lookup_popup,
        _scim_config->read(String(HONOKA_CONFIG_KEY_LOOKUPPOPUP),
            String(HONOKA_DEFAULT_KEY_LOOKUPPOPUP)));
    scim_string_to_key_list(k_lookup_pageup,
        _scim_config->read(String(HONOKA_CONFIG_KEY_LOOKUPPAGEUP),
            String(HONOKA_DEFAULT_KEY_LOOKUPPAGEUP)));
    scim_string_to_key_list(k_lookup_pagedown,
        _scim_config->read(String(HONOKA_CONFIG_KEY_LOOKUPPAGEDOWN),
            String(HONOKA_DEFAULT_KEY_LOOKUPPAGEDOWN)));
    scim_string_to_key_list(k_convert_hiragana,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERT_HIRAGANA),
            String(HONOKA_DEFAULT_KEY_CONVERT_HIRAGANA)));
    scim_string_to_key_list(k_convert_katakana,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERT_KATAKANA),
            String(HONOKA_DEFAULT_KEY_CONVERT_KATAKANA)));
    scim_string_to_key_list(k_convert_half,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERT_HALF),
            String(HONOKA_DEFAULT_KEY_CONVERT_HALF)));
    scim_string_to_key_list(k_convert_wide,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERT_WIDE),
            String(HONOKA_DEFAULT_KEY_CONVERT_WIDE)));

    scim_string_to_key_list(k_conversion_rensou,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_RENSOU),
            String(HONOKA_DEFAULT_KEY_CONVERSION_RENSOU)));
    scim_string_to_key_list(k_conversion_ikeiji,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERSION_IKEIJI),
            String(HONOKA_DEFAULT_KEY_CONVERSION_IKEIJI)));
    scim_string_to_key_list(k_select_yosoku,
        _scim_config->read(String(HONOKA_CONFIG_KEY_SELECT_YOSOKU),
            String(HONOKA_DEFAULT_KEY_SELECT_YOSOKU)));
    scim_string_to_key_list(k_auto_conversion,
        _scim_config->read(String(HONOKA_CONFIG_KEY_AUTO_CONVERSION),
            String(HONOKA_DEFAULT_KEY_AUTO_CONVERSION)));
    scim_string_to_key_list(k_next_convertor,
        _scim_config->read(String(HONOKA_CONFIG_KEY_NEXT_CONVERTOR),
            String(HONOKA_DEFAULT_KEY_NEXT_CONVERTOR)));
    scim_string_to_key_list(k_prev_convertor,
        _scim_config->read(String(HONOKA_CONFIG_KEY_PREV_CONVERTOR),
            String(HONOKA_DEFAULT_KEY_PREV_CONVERTOR)));

    scim_string_to_key_list(k_reconversion,
        _scim_config->read(String(HONOKA_CONFIG_KEY_RECONVERSION),
            String(HONOKA_DEFAULT_KEY_RECONVERSION)));
    scim_string_to_key_list(k_result_to_preedit,
        _scim_config->read(String(HONOKA_CONFIG_KEY_RESULTTOPREEDIT),
            String(HONOKA_DEFAULT_KEY_RESULTTOPREEDIT)));
    scim_string_to_key_list(k_multi_conversion,
        _scim_config->read(String(HONOKA_CONFIG_KEY_MULTI_CONVERSION),
            String(HONOKA_DEFAULT_KEY_MULTI_CONVERSION)));
    for(unsigned i = 0;i < 10;i ++) {
        char a[3];
        sprintf(a,"%d",i);
        scim_string_to_key_list(k_selection[i],
            _scim_config->read(String(HONOKA_CONFIG_KEY_SELECTION_PREFIX) + String(a),
                String(a)));
    }


    changePreEditor(defaultPreEditor);
    changeConvertor(defaultConvertor);
    changePredictor(defaultPredictor);
    
    // ここで一度接続しておこう。
    m_convertor->connect();

}


/*!
    \fn HonokaInstance::changePreEditor(const String &name)
 */
void HonokaInstance::changePreEditor(const String &name)
{
    // PreEditorを変更するメソッド。
    for(unsigned int i = 0;i < preeditors.size();i ++) {
        if (preeditors[i]->getPropertyName() == name) {
            m_preeditor = preeditors[i];
            return;
        }
    }
    return;
}



/*!
    \fn HonokaInstance::changeConvertor(const String &name)
 */
void HonokaInstance::changeConvertor(const String &name)
{
    // Convertorを変更するメソッド。
    for(unsigned int i = 0;i < convertors.size();i ++) {
        if (convertors[i]->getPropertyName() == name) {
            m_convertor = convertors[i];
            return;
        }
    }
    return;
}



/*!
    \fn HonokaInstance::changePredictor(const String &name)
 */
void HonokaInstance::changePredictor(const String &name)
{
    // Predictorを変更するメソッド。
    for(unsigned int i = 0;i < predictors.size();i ++) {
        if (predictors[i]->getPropertyName() == name) {
            m_predictor = predictors[i];
            return;
        }
    }
    return;
}


/*!
    \fn HonokaInstance::updateProperty()
 */
void HonokaInstance::updateProperty()
{
    // プロパティを更新するメソッド。
    if (m_proplist.empty()) {
        Property p;
        p = Property(HONOKA_PROP_INPUTMODE,"",String(""),_("input mode"));
        m_proplist.push_back(p);
        for(unsigned int i = 0;i < preeditors.size();i ++) {
            p = Property(String(HONOKA_PROP_INPUTMODE) + String("/") + preeditors[i]->getPropertyName(),
                preeditors[i]->getName(),String(""),_("mode status"));
            m_proplist.push_back(p);
        }
        
        p = Property(HONOKA_PROP_PREDICTOR,"",String(""),_("predictor"));
        m_proplist.push_back(p);
        for(unsigned int i = 0;i < predictors.size();i ++) {
            p = Property(String(HONOKA_PROP_PREDICTOR) + String("/") + predictors[i]->getPropertyName(),
                predictors[i]->getName(),String(""),_("mode status"));
            m_proplist.push_back(p);
        }
        
        p = Property(HONOKA_PROP_CONVERTOR,"",String(""),_("convertor"));
        m_proplist.push_back(p);
        for(unsigned int i = 0;i < convertors.size();i ++) {
            p = Property(String(HONOKA_PROP_CONVERTOR) + String("/") + convertors[i]->getPropertyName(),
                convertors[i]->getName(),String(""),_("mode status"));
            m_proplist.push_back(p);
        }
        p = Property(HONOKA_PROP_MODESTATUS,"",String(""),_("mode status"));
        m_proplist.push_back(p);
        p = Property(HONOKA_PROP_CONVERSIONMODE,"",String(""),_("conversion mode"));
        m_proplist.push_back(p);
    }
    PropertyList::iterator it;
    it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_INPUTMODE);
    if (it != m_proplist.end()) {
        it->set_label(m_preeditor->getName());
    }
    update_property(*it);
    it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_PREDICTOR);
    if (it != m_proplist.end()) {
        it->set_label(m_predictor->getName());
    }
    update_property(*it);
    it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_CONVERTOR);
    if (it != m_proplist.end()) {
        it->set_label(m_convertor->getName());
    }
    update_property(*it);
    it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_MODESTATUS);
    if (it != m_proplist.end()) {
        if (m_conversion) it->set_label(_("Kanji"));
        else if (m_yosoku) it->set_label(_("Yosoku"));
        else it->set_label(m_preeditor->getModeName());
    }
    update_property(*it);
    it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_CONVERSIONMODE);
    if (it != m_proplist.end()) {
        if (auto_conversion) it->set_label(_("AUTO"));
        else  it->set_label(_("REN"));
    }
    update_property(*it);
    register_properties(m_proplist);
}


/*!
    \fn HonokaInstance::updatePreEditor()
 */
void HonokaInstance::updatePreEditor()
{
    // PreEditorを更新するメソッド。
    // 頻繁に呼ばれます。
    if (m_preeditor->getTextLength()) {
        if (auto_conversion && (m_preeditor->getTextLength() == m_preeditor->getPos()))
            autoConversion();
        else {
            show_preedit_string();
            update_preedit_string(m_preeditor->getText(),m_preeditor->getAttributeList());
            update_preedit_caret(m_preeditor->getPos());
        }
        // 予測専用コード。現状Wnn7向け。分離予定。
        //if ((!m_conversion) && yosoku && m_convertor->isConnected() && (sType == "Wnn7") && 
        //    (!auto_conversion) && (m_convertor->getName() == "Wnn")) {
        //    m_convList = m_convertor->getYosokuList(m_preeditor->getText());
        if ((!m_conversion) && yosoku && m_predictor->isConnected() && (!auto_conversion)) {
            m_convList = m_predictor->getPredictionList(m_preeditor->getText());
            if (m_convList.count) {
                m_lookup_table.clear();
                for(unsigned int i = 0;i < m_convList.count;i ++) {
                    m_lookup_table.append_candidate(m_convList.kouho.at(i).kanji);
                }
                startLookup();
            } else {
                //m_lookup_table.clear();
                hide_lookup_table();
            }
        } else hide_lookup_table();
    } else {
        hide_preedit_string();
        hide_lookup_table();
    }
    // mini_status用コード。怪しい。
    if (mini_status) {
        update_aux_string(utf8_mbstowcs(m_preeditor->getModeName()));
        show_aux_string();
    } else {
        hide_aux_string();
    }

    updateProperty();
}
/*!
    \fn HonokaInstance::updateConversion()
 */
void HonokaInstance::updateConversion()
{
    // 変換時の表示更新。
    update_preedit_string(m_convertor->getText(),m_convertor->getAttributeList());
    update_preedit_caret(m_convertor->getCaretPos());
    if (m_lookup) {
        m_lookup_table.set_cursor_pos(m_convList.pos);
        update_lookup_table(m_lookup_table);
        show_lookup_table();
    } else {
        hide_lookup_table();
        hide_aux_string();
    }
    updateProperty();
}


bool HonokaInstance::process_key_event (const KeyEvent& key)
{
    // キーイベント処理。
    if (key.is_key_release()) return false;
    KeyEvent ke = key;
    if (ke.mask & SCIM_KEY_CapsLockMask) ke.mask -= SCIM_KEY_CapsLockMask;
    if (ke.mask & SCIM_KEY_NumLockMask) ke.mask -= SCIM_KEY_NumLockMask;
    // if (ke.mask & SCIM_KEY_ScrollLockMask) ke.mask -= SCIM_KEY_ScrollLockMask;
    if (m_conversion) return process_conversion_key_event(ke);
    else if (m_yosoku) return process_yosoku_key_event(ke);
    else return process_preedit_key_event(ke) ;
}


/*!
    \fn HonokaInstance::process_preedit_key_event(const KeyEvent &key)
 */
bool HonokaInstance::process_preedit_key_event(const KeyEvent &key)
{
    // PreEdit時のキーイベント。
    // PreEditorのキーイベントフック。
    if (m_preeditor->keyEventHook(key)) {
        updatePreEditor();
        return true;
    } else

    if (k_reconversion.comp(key)) {
        WideString w;
        int c;
        if (get_surrounding_text(w,c)) {
            if (w.length()) {
                // delete_surrounding_text()を使っての削除とかいるね。
                startConversion(w);
                alp_count ++;
                return true;
            }
        }
    }
    
    // バッファが空では無い場合。
    if (m_preeditor->getTextLength()) {
        if (k_conversion_start.comp(key)) {
            startConversion(m_preeditor->getText(true));
            alp_count ++;
            return true;
        } else
        if (k_multi_conversion.comp(key)) {
            startConversion(m_preeditor->getText(true),true);
            alp_count ++;
            return true;
        } else
        if (k_commit.comp(key)) {
            if (auto_conversion) {
                commit_string(m_convertor->getText());
                m_convertor->reset();
            } else commit_string(m_preeditor->getText(true));
            m_preeditor->reset();
            while(preeditStack.size()) {
                preeditStack.pop();
            }
            updatePreEditor();
            return true;
        } else
        if (k_forward.comp(key) || k_backward.comp(key)) {
            k_backward.comp(key) ? m_preeditor->setPos(m_preeditor->getPos() - 1) : m_preeditor->setPos(m_preeditor->getPos() + 1);
            updatePreEditor();
            return true;
        } else
        if (k_home.comp(key) || k_end.comp(key)) {
            k_end.comp(key) ? m_preeditor->setPos(m_preeditor->getTextLength()) : m_preeditor->setPos(0);
            updatePreEditor();
            return true;
        } else
        if ((k_backspace.comp(key)) || (k_delete.comp(key))) {
            k_backspace.comp(key) ? m_preeditor->backspace(): m_preeditor->del();
            updatePreEditor();
            return true;
        } else
        if (k_convert_hiragana.comp(key)) {
            m_preeditor->kataHira();
            updatePreEditor();
            return true;
        } else
        if (k_convert_katakana.comp(key)) {
            m_preeditor->hiraKata();
            updatePreEditor();
            return true;
        } else
        if (k_convert_half.comp(key)) {
            m_preeditor->toHalf();
            updatePreEditor();
            return true;
        } else
        if (k_convert_wide.comp(key)) {
            m_preeditor->toWide();
            updatePreEditor();
            return true;
        } else
        if (k_select_yosoku.comp(key) && yosoku) {
            if ((m_convList.kType == YOSOKU) && (m_convList.count)) {
                return process_yosoku_key_event(key);
            }
        }
    }

    // バッファの存在にかかわらず。
    if (k_cancel.comp(key)) {
        if (m_preeditor->cancelEvent()) {
            updatePreEditor();
            return true;
        }
        if (preeditStack.size()) {
            m_preeditor->reset();
            m_preeditor->setText(preeditStack.top());
            m_preeditor->setPos(preeditStack.top().length());
            preeditStack.pop();
        } else 
        if (m_preeditor->getTextLength()) {
            m_preeditor->reset();
        }
        updatePreEditor();
        return true;
    }

    if (k_next_convertor.comp(key) || k_prev_convertor.comp(key)) {
        for(unsigned int i = 0;i < convertors.size();i ++) {
            if (convertors[i]->getPropertyName() == m_convertor->getPropertyName()) {
                if (k_next_convertor.comp(key)) {
                    if (i == (convertors.size() - 1)) i = 0;
                    else i ++;
                } else {
                    if (i == 0) i = convertors.size() - 1;
                    else i --;
                }
                changeConvertor(convertors[i]->getPropertyName());
                updatePreEditor();
                return true;
            }
        }
    }
    
    if (k_auto_conversion.comp(key)) {
        auto_conversion ? auto_conversion = false : auto_conversion = true;
        updatePreEditor();
        return true;
    }

    if (m_preeditor->inputEvent(key)) {
        updatePreEditor();

        return true;
    } else {
        // preeditorで処理できなかった場合はやはりcommitしてアプリケーションに返すべきだ。
        if (m_preeditor->getTextLength()) {
            if (auto_conversion) {
                commit_string(m_convertor->getText());
                m_convertor->reset();
            } else commit_string(m_preeditor->getText(true));
        }
        m_preeditor->reset();
        updatePreEditor();
    }

    return false;
}

/*!
    \fn HonokaInstance::process_conversion_key_event(const KeyEvent &key)
 */
bool HonokaInstance::process_conversion_key_event(const KeyEvent &key)
{
    // 変換時のキーイベント処理。
    // 喰う!。
    if ((key.code == SCIM_KEY_Shift_L) ||
        (key.code == SCIM_KEY_Shift_R) ||
        (key.code == SCIM_KEY_Control_L) ||
        (key.code == SCIM_KEY_Control_R) ||
        (key.code == SCIM_KEY_Alt_L) ||
        (key.code == SCIM_KEY_Alt_R) ||
        (key.code == SCIM_KEY_Super_L) ||
        (key.code == SCIM_KEY_Super_R) ||
        (key.code == SCIM_KEY_Hyper_L) ||
        (key.code == SCIM_KEY_Hyper_R)) return true;

    if ((alp <= alp_count) && (alp != 0)) {
        if (!m_lookup) startLookup();
    }

    if (k_lookup_popup.comp(key)) {
        if (m_lookup) return true;
        startLookup();
        return true;
    } else
    if (k_lookup_pageup.comp(key)) {
        if (m_lookup) {
            lookup_table_page_up();
        }
    } else
    if (k_lookup_pagedown.comp(key)) {
        if (m_lookup) {
            lookup_table_page_down();
        }
    } else
    if (k_commit.comp(key)) {
        commit_string(m_convertor->getText());
        while(preeditStack.size()) {
            preeditStack.pop();
        }
        m_convertor->updateFrequency();
        m_preeditor->reset();
        m_convertor->reset();
        m_conversion = false;
        updatePreEditor();
        m_lookup = false;
        alp_count = 1;
        if (m_def_convertor != m_convertor) m_convertor = m_def_convertor;
        updateProperty();
        return true;
    } else
    if (k_result_to_preedit.comp(key)) {
        preeditStack.push(m_preeditor->getText());
        m_preeditor->reset();
        m_preeditor->setText(m_convertor->getText());
        m_preeditor->setPos(m_convertor->getText().length());
        m_convertor->updateFrequency();
        m_convertor->reset();
        m_conversion = false;
        m_lookup = false;
        alp_count = 1;
        if (m_def_convertor != m_convertor) m_convertor = m_def_convertor;
        updateProperty();
        updatePreEditor();
        return true;
    } else
    if (k_cancel.comp(key) || k_backspace.comp(key)) {
        m_convertor->reset();
        m_conversion = false;
        m_lookup = false;
        alp_count = 0;
        if (m_def_convertor != m_convertor) m_convertor = m_def_convertor;
        updateProperty();
        updatePreEditor();
        return true;
    } else
    if (k_conversion_next.comp(key) || k_conversion_prev.comp(key)) {
        k_conversion_prev.comp(key) ? m_convList.pos --: m_convList.pos ++;
        if (m_convList.pos >= m_convList.count) m_convList.pos = 0;
        else if (m_convList.pos < 0) m_convList.pos = m_convList.count - 1;
        alp_count ++;

        m_convertor->select(m_convList.pos);
        updateConversion();
        return true;
    } else
    if (k_conversion_expand.comp(key) || k_conversion_shrink.comp(key)) {
        bool r;
        k_conversion_shrink.comp(key) ? r = m_convertor->resizeRegion(-1) : r = m_convertor->resizeRegion(1);
        if (!r) return true;
        m_convList = m_convertor->getResultList();
        if (alp == -1) {
            startLookup();
        } else m_lookup = false;
        updateConversion();
        alp_count = 1;
        return true;
    } else
    if (k_conversion_rensou.comp(key) && ((sType == "Wnn7") || (sType == "Wnn8")) ) {
        m_convList = m_convertor->getResultList(m_convertor->getPos(),RENSOU);
        if (m_convList.count == 0) return true;
        startLookup();
        updateConversion();
        alp_count = 1;
        return true;
    } else
    if (k_conversion_ikeiji.comp(key) && ((sType == "Wnn7") || (sType == "Wnn8")) ) {
        m_convList = m_convertor->getResultList(m_convertor->getPos(),IKEIJI);
        if (m_convList.count == 0) return true;
        startLookup();
        updateConversion();
        alp_count = 1;
        return true;
    } else
    if (k_conversion_forward.comp(key) || k_conversion_backward.comp(key)) {
        k_conversion_backward.comp(key) ? m_convertor->setPos(m_convertor->getPos() - 1) : m_convertor->setPos(m_convertor->getPos() + 1);
        m_convList = m_convertor->getResultList();
        if (alp == -1) {
            startLookup();
        } else m_lookup = false;
        updateConversion();
        alp_count = 1;
        return true;
    } else
    if (k_convert_hiragana.comp(key) || k_convert_katakana.comp(key)) {
        WideString res = m_convList.Yomi;
        k_convert_hiragana.comp(key) ? m_preeditor->convKataHira(res) : m_preeditor->convHiraKata(res);
        for(unsigned int i = 0;i < m_convList.count;i ++) {
            if (res == m_convList.kouho[i].kanji) {
                m_convList.pos = i;
                m_convertor->select(m_convList.pos);
                updateConversion();
                break;
            }
        }
        return true;
    } else
    if ((k_next_convertor.comp(key) || k_prev_convertor.comp(key)) && (m_convertor != m_multi)) {
        for(unsigned int i = 0;i < convertors.size();i ++) {
            if (convertors[i]->getPropertyName() == m_convertor->getPropertyName()) {
                if (k_next_convertor.comp(key)) {
                    if (i == (convertors.size() - 1)) i = 0;
                    else i ++;
                } else {
                    if (i == 0) i = convertors.size() - 1;
                    else i --;
                }
                changeConvertor(convertors[i]->getPropertyName());
                if (m_def_convertor != m_convertor) {
                    m_def_convertor->reset();
                    startConversion();
                }
                return true;
            }
        }
    } else 
    if (numkeyselect && m_lookup) {
        for(unsigned int i = 0;i < 10;i ++) {
            if (k_selection[i].comp(key)) {
                int numc = i - 1;
                if (numc < 0) numc = 9;
                if (m_lookup_table.get_current_page_size() <= numc) return true;
                m_convList.pos = numc + m_lookup_table.get_current_page_start();
                m_convertor->select(m_convList.pos);
                m_convertor->setPos(m_convertor->getPos() + 1);
                m_convList = m_convertor->getResultList();
                updateConversion();
                startLookup();
                return true;
            }
        }
    }
    
    if (key.get_ascii_code()) {
        /*
        if (key.mask & (SCIM_KEY_AltMask | SCIM_KEY_ControlMask)) return false;
            if (m_lookup && numkeyselect) {
                char numk[] = "1234567890";
                int numc = 0;
                bool numkey = false;
                for(;numk[numc]; ++ numc) {
                    if (key.get_ascii_code() == numk[numc]) {
                        numkey = true;
                        break;
                    }
                }
                if (numkey) {
                    if (m_lookup_table.get_current_page_size() < numc) return true;
                    m_convList.pos = numc + m_lookup_table.get_current_page_start();
                    m_convertor->select(m_convList.pos);
                    m_convertor->setPos(m_convertor->getPos() + 1);
                    m_convList = m_convertor->getResultList();
                    updateConversion();
                    startLookup();
                    return true;
                }
            }
        */
        commit_string(m_convertor->getText());
        m_convertor->updateFrequency();
        m_preeditor->reset();
        m_convertor->reset();
        m_conversion = false;
        m_lookup = false;
        alp_count = 0;
        if (m_def_convertor != m_convertor) m_convertor = m_def_convertor;
        updateProperty();
        updatePreEditor();
        return process_preedit_key_event(key);
    }


    return true;
}



/*!
    \fn HonokaInstance::process_yosoku_key_event(const KeyEvent &key)
 */
bool HonokaInstance::process_yosoku_key_event(const KeyEvent &key)
{
    // 予測選択時のキーイベント処理。
    if (!m_yosoku) {
        m_yosoku = true;
        update_preedit_string(m_convList.kouho.at(m_convList.pos).kanji);
        update_preedit_caret(0);
        show_preedit_string();
        updateProperty();
        return true;
    }

    if (k_lookup_pageup.comp(key)) {
        lookup_table_page_up();
        return true;
    } else
    if (k_lookup_pagedown.comp(key)) {
        lookup_table_page_down();
        return true;
    } else
    if (k_conversion_next.comp(key) || k_conversion_prev.comp(key) || k_select_yosoku.comp(key)) {
        k_conversion_prev.comp(key) ? m_convList.pos --: m_convList.pos ++;
        if (m_convList.pos >= m_convList.count) m_convList.pos = 0;
        else if (m_convList.pos < 0) m_convList.pos = m_convList.count - 1;
        m_lookup_table.set_cursor_pos(m_convList.pos);
        update_lookup_table(m_lookup_table);
        update_preedit_string(m_convList.kouho.at(m_convList.pos).kanji);
        update_preedit_caret(0);
        updateProperty();
        return true;
    } else
    if (k_commit.comp(key)) {
        m_yosoku = false;
        commit_string(m_convList.kouho.at(m_convList.pos).kanji);
        m_preeditor->reset();
        updatePreEditor();
        return true;
    } else
    if (k_cancel.comp(key) || k_backspace.comp(key)) {
        m_yosoku = false;
        updatePreEditor();
        return true;
    }
    if (numkeyselect) {
        for(unsigned int i = 0;i < 10;i ++) {
            if (k_selection[i].comp(key)) {
                int numc = i - 1;
                if (numc < 0) numc = 9;
                if (m_lookup_table.get_current_page_size() <= numc) return true;
                select_candidate((unsigned int)numc);
                return true;
            }
        }
    }

    if (!key.get_ascii_code()) return true;
/*    if ((!(key.is_control_down() || key.is_alt_down() || key.is_shift_down())) && numkeyselect) {
        char numk[] = "1234567890";
        for(int numc = 0;numk[numc]; ++ numc) {
            if (key.get_ascii_code() == numk[numc]) {
                if (m_lookup_table.get_current_page_size() > numc)
                    select_candidate((unsigned int)numc);
                return true;
            }
        }
    }
*/
    m_yosoku = false;
    commit_string(m_convList.kouho.at(m_convList.pos).kanji);
    m_preeditor->reset();
    updatePreEditor();
    return process_preedit_key_event(key);
}



void HonokaInstance::move_preedit_caret (unsigned int pos)
{
    //if (!m_conversion) m_preeditor->setPos(pos);
    //update_preedit_caret(pos);
}

void HonokaInstance::select_candidate (unsigned int item)
{
    if (!m_lookup_table.number_of_candidates()) return;

    int p = m_lookup_table.get_current_page_start() + item;
    m_convList.pos = p;
    m_convertor->select(m_convList.pos);
    if (m_convList.kType != YOSOKU) {
        update_preedit_string(m_convertor->getText(),m_convertor->getAttributeList());
        update_preedit_caret(m_convertor->getCaretPos());
    } else {
        update_preedit_string(m_convList.kouho.at(m_convList.pos).kanji);
        update_preedit_caret(0);
        if (!m_yosoku) {
            // マウスで選択した場合は予測選択モードに突入。
            m_yosoku = true;
            show_preedit_string();
            updateProperty();
        }
    }
    m_lookup_table.set_cursor_pos(m_convList.pos);
    update_lookup_table(m_lookup_table);
}

void HonokaInstance::update_lookup_table_page_size (unsigned int page_size)
{
    m_lookup_table.set_page_size (page_size);
}

void HonokaInstance::lookup_table_page_up ()
{
    if (!m_lookup_table.number_of_candidates () || !m_lookup_table.get_current_page_start ()) return;

    int p = m_convList.pos - m_lookup_table.get_current_page_size();
    if (p < 0) p = 0;
    m_convList.pos = p;
    m_convertor->select(m_convList.pos);
    update_preedit_string(m_convertor->getText(),m_convertor->getAttributeList());
    update_preedit_caret(m_convertor->getCaretPos());
    m_lookup_table.set_cursor_pos(m_convList.pos);
    update_lookup_table(m_lookup_table);
}

void HonokaInstance::lookup_table_page_down ()
{
    if (!m_lookup_table.number_of_candidates () ||
        m_lookup_table.get_current_page_start () + m_lookup_table.get_current_page_size () >=
          m_lookup_table.number_of_candidates ())
        return;

    int p = m_convList.pos + m_lookup_table.get_current_page_size();
    if (p >= m_convList.count) p = m_convList.count - 1;
    m_convList.pos = p;
    m_convertor->select(m_convList.pos);
    update_preedit_string(m_convertor->getText(),m_convertor->getAttributeList());
    update_preedit_caret(m_convertor->getCaretPos());
    m_lookup_table.set_cursor_pos(m_convList.pos);
    update_lookup_table(m_lookup_table);
}

void HonokaInstance::reset ()
{
    // Qt-immodule+scim-qtimmではこのメソッドが大量に呼ばれるようだが、他ではどうなのか?。
    alp_count = 0;
    m_conversion = false;
    m_lookup = false;
    m_convertor->reset();
    m_preeditor->reset();
    //if (m_convertor->isConnected()) m_convertor->wnnDisconnect();
    //m_convertor->wnnConnect(String("scim-wnn"),host,rc,sType,0);
    //updatePreEditor();
}

void HonokaInstance::focus_in ()
{
    if (!m_conversion) updatePreEditor();
    else updateProperty();
}

void HonokaInstance::focus_out ()
{
    // フォーカスを失った時は全てコミット!。
    if (m_conversion) {
        commit_string(m_convertor->getText());
        m_convertor->updateFrequency();
        m_convertor->reset();
        if (m_def_convertor != m_convertor) m_convertor = m_def_convertor;
        m_conversion = false;
        m_lookup = false;
        alp_count = 0;
    } else if (m_preeditor->getTextLength()) {
        commit_string(m_preeditor->getText(true));
    }
    m_preeditor->reset();
    updatePreEditor();
}

void HonokaInstance::trigger_property (const String &property)
{
    String s = HONOKA_PROP_INPUTMODE;
    if ((property.length() > s.length()) && (property.substr(0,s.length()) == s)) {
        changePreEditor(property.substr(s.length() + 1));
        updateProperty();
    }
    
    s = HONOKA_PROP_PREDICTOR;
    if ((property.length() > s.length()) && (property.substr(0,s.length()) == s)) {
        changePredictor(property.substr(s.length() + 1));
        updateProperty();
    }
    
    s = HONOKA_PROP_CONVERTOR;
    if ((property.length() > s.length()) && (property.substr(0,s.length()) == s)) {
        changeConvertor(property.substr(s.length() + 1));
        if (m_conversion) {
            if (m_def_convertor != m_convertor) {
                m_def_convertor->reset();
                startConversion();
            }
        }
        updateProperty();
    }
    s = HONOKA_PROP_CONVERSIONMODE;
    if (property == s) {
        auto_conversion ? auto_conversion = false : auto_conversion = true;
        updatePreEditor();
    }
}






/*!
    \fn HonokaInstance::startConversion(WideString s,bool multi)
 */
void HonokaInstance::startConversion(WideString s,bool multi)
{
    // 変換開始処理。
    // 一度lookupは消しておこう。
    m_def_convertor = m_convertor;
    if (multi) m_convertor = m_multi;
    if (!s.length()) s = yomi;
    else yomi = s;
    m_lookup = false;
    m_lookup_table.clear();
    hide_lookup_table();

    hide_aux_string();
    if (!m_convertor->isConnected()) {
        if (!m_convertor->connect()) {
            update_aux_string(utf8_mbstowcs(String(_("could not connect to jserver."))));
            show_aux_string();
            return;
        }
    }

    m_convertor->setYomiText(s);
    if (m_convertor->ren_conversion() == -1) {
        update_aux_string(utf8_mbstowcs(String(_("could not connect to jserver."))));
        show_aux_string();
        return;
    }
    m_convList.kouho.clear();
    m_convList = m_convertor->getResultList();
    m_conversion = true;

    alp_count = 1;

    show_preedit_string();
    update_preedit_string(m_convertor->getText(),m_convertor->getAttributeList());
    update_preedit_caret(m_convertor->getCaretPos());
    if (alp == -1) {
        startLookup();
    }
    updateProperty();
}


/*!
    \fn HonokaInstance::autoConversion()
 */
void HonokaInstance::autoConversion()
{
    // 自動変換処理コード。
    if (!m_convertor->isConnected()) {
        if (!m_convertor->connect()) {
            update_aux_string(utf8_mbstowcs(String(_("could not connect to jserver."))));
            show_aux_string();
            show_preedit_string();
            update_preedit_string(m_preeditor->getText(),m_preeditor->getAttributeList());
            update_preedit_caret(m_preeditor->getPos());
            return;
        }
    }

    m_convertor->reset();
    m_convertor->setYomiText(m_preeditor->getText(true));
    if (m_convertor->ren_conversion() == -1) {
        update_aux_string(utf8_mbstowcs(String(_("could not connect to jserver."))));
        show_aux_string();
        show_preedit_string();
        update_preedit_string(m_preeditor->getText(),m_preeditor->getAttributeList());
        update_preedit_caret(m_preeditor->getPos());
        return;
    }

    show_preedit_string();
    update_preedit_string(m_convertor->getText());
    update_preedit_caret(m_convertor->getText().size());
}


/*!
    \fn HonokaInstance::createLookupTable(ResultList cList)
 */
void HonokaInstance::createLookupTable(ResultList cList)
{
    // 候補一覧を作る。
    hide_lookup_table();
    m_lookup_table.clear();
    if (!cList.count) return;
    for (unsigned int i = 0;i < cList.count;i ++) {
        if (cList.kouho.at(i).label.length()) m_lookup_table.append_candidate(cList.kouho.at(i).label);
        else m_lookup_table.append_candidate(cList.kouho.at(i).kanji);
    }
    m_lookup_table.set_cursor_pos(cList.pos);
    update_lookup_table(m_lookup_table);
}


/*!
    \fn HonokaInstance::startLookup()
 */
void HonokaInstance::startLookup()
{
    // 候補一覧を表示する。
    createLookupTable(m_convList);
    if (m_convList.count == 0) return;
    m_lookup = true;
    update_aux_string(m_convList.Title);
    show_aux_string();
    show_lookup_table();
}