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 "honokatimer.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

#ifndef PACKAGE_STRING
 #define PACKAGE_STRING "honoka"
#endif



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-2006 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(PACKAGE_STRING) + String("\n") + String(_("HONOKA-HELP")));
}

String HonokaFactory::get_uuid () const
{
    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");
    
    // 初期化へ。
    loadPlugins();
    init();
}

HonokaInstance::~HonokaInstance()
{
    HonokaTimer::destruct();
    if (save_setting) {
        _scim_config->write(String(HONOKA_PREVIOUS_PREEDITOR),m_preeditor->getName());
        _scim_config->write(String(HONOKA_PREVIOUS_PREDICTOR),m_predictor->getName());
        _scim_config->write(String(HONOKA_PREVIOUS_CONVERTOR),m_convertor->getName());
    }
    for(unsigned int i = 0;i < plugins.size();i ++) {
        plugins[i].deleteInstance(plugins[i].instance);
        dlclose(plugins[i].dll);
    }
    delete acpredictor;
    delete m_multi;
}

/*!
    \fn HonokaInstance::split(const String &str,const char &sep)
 */
vector<String> HonokaInstance::split(const String &str,const char &sep)
{
    vector<String> s;
    String w;
    for(unsigned int i = 0;i < str.length();i ++) {
        if (str[i] == sep) {
            if (w.length()) {
                s.push_back(w);
                w.clear();
            }
        } else w = w + str.substr(i,1);
    }
    if (w.length()) s.push_back(w);
    return s;
}

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;
}


/*!
    \fn HonokaInstance::loadPlugins()
 */
void HonokaInstance::loadPlugins()
{
    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 (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);
                }
            }
        }
        closedir(dir);
    }
    
    acpredictor = new ACPredictor(_scim_config,this);
    predictors.push_back(acpredictor);
    
    // プラグインがなければデフォの低機能なベースクラスを使います。
    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);
    
    return;
}


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

    m_conversion = false;
    m_prediction = 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);
    prediction = _scim_config->read(String(HONOKA_CONFIG_PREDICTION),HONOKA_DEFAULT_PREDICTION);
    realtime_prediction = _scim_config->read(String(HONOKA_CONFIG_REALTIME_PREDICTION),HONOKA_DEFAULT_REALTIME_PREDICTION);
    if (m_predictor->getName() == "Predictor") prediction = false;
    
    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);;
    predictionDelay = _scim_config->read(String(HONOKA_CONFIG_PREDICTION_DELAY),HONOKA_DEFAULT_PREDICTION_DELAY);
    save_setting = _scim_config->read(String(HONOKA_CONFIG_SAVE_SETTING),HONOKA_DEFAULT_SAVE_SETTING);
    select_prediction_direct = _scim_config->read(String(HONOKA_CONFIG_SELECT_PREDICTION_DIRECT),HONOKA_DEFAULT_SELECT_PREDICTION_DIRECT);

    // デフォルトキー設定。
    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_furigana_commit,
        _scim_config->read(String(HONOKA_CONFIG_KEY_FURIGANA_COMMIT),
            String(HONOKA_DEFAULT_KEY_FURIGANA_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_ctype,
        _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERT_CTYPETOGGLE),
            String(HONOKA_DEFAULT_KEY_CONVERT_CTYPETOGGLE)));
    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_prediction,
        _scim_config->read(String(HONOKA_CONFIG_KEY_SELECT_PREDICTION),
            String(HONOKA_DEFAULT_KEY_SELECT_PREDICTION)));
    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)));
    }

    vector<String> sl;
    
    sl = split(defaultPreEditor);
    for(unsigned int i = 0;i < sl.size();i ++) if (changePreEditor(sl[i])) break;
    sl = split(defaultConvertor);
    for(unsigned int i = 0;i < sl.size();i ++) if (changeConvertor(sl[i])) break;
    sl = split(defaultPredictor);
    for(unsigned int i = 0;i < sl.size();i ++) if (changePredictor(sl[i])) break;
    
    for(unsigned int i = 0;i < convertors.size();i ++) {
        HonokaKeyEventList k;
        scim_string_to_key_list(k,
            _scim_config->read(String(HONOKA_CONFIG_KEY_CONVERTOR_PREFIX) + String("/") + convertors[i]->getName(),String("")));
        k_convertor.push_back(k);
    }
    for(unsigned int i = 0;i < preeditors.size();i ++) {
        HonokaKeyEventList k;
        scim_string_to_key_list(k,
            _scim_config->read(String(HONOKA_CONFIG_KEY_PREEDITOR_PREFIX) + String("/") + preeditors[i]->getName(),String("")));
        k_preeditor.push_back(k);
    }
    for(unsigned int i = 0;i < predictors.size();i ++) {
        HonokaKeyEventList k;
        scim_string_to_key_list(k,
            _scim_config->read(String(HONOKA_CONFIG_KEY_PREDICTOR_PREFIX) + String("/") + predictors[i]->getName(),String("")));
        k_predictor.push_back(k);
    }
    
    if (save_setting) {
        changePreEditor(_scim_config->read(String(HONOKA_PREVIOUS_PREEDITOR),String()));
        changePredictor(_scim_config->read(String(HONOKA_PREVIOUS_PREDICTOR),String()));
        changeConvertor(_scim_config->read(String(HONOKA_PREVIOUS_CONVERTOR),String()));
    }

}



/*!
    \fn HonokaInstance::getConvertedText()
 */
const WideString HonokaInstance::getConvertedText()
{
    // このかたちでは、変換動作から候補一覧を取得した上でないと結果を得られない。
    // 直すべきか?。
    vector<Segment> seglist = m_convertor->getSegmentList();
    vector<Segment> newsegs;
    WideString t;
    for(unsigned int i = 0;i < seglist.size();i ++) {
        if ((i < m_convertor->getPos()) && (segments.size() > i))  {
            if ((seglist[i].getKanji() != segments[i].getKanji()) && (seglist[i].getYomi().length() == segments[i].getYomi().length()))
                newsegs.push_back(segments[i]);
            else newsegs.push_back(seglist[i]);
        } else if (i == m_convertor->getPos())
            newsegs.push_back(Segment(m_convList.kouho[m_convList.pos].kanji,seglist[i].getYomi()));
        else newsegs.push_back(seglist[i]);
    }
    segments = newsegs;
    for(unsigned int i = 0;i < segments.size();i ++) {
        t += segments[i].getKanji();
    }
    return t;
}


/*!
    \fn HonokaInstance::getConvertedAttributeList()
 */
const AttributeList HonokaInstance::getConvertedAttributeList()
{
    AttributeList attr;
    int c = 0;
    for(unsigned int i = 0;i < segments.size();i ++) {
        if (m_convertor->getPos() == i) {
            Attribute a(c,segments[i].getKanji().length(),SCIM_ATTR_DECORATE,SCIM_ATTR_DECORATE_REVERSE);
            attr.push_back(a);
            break;
        } else c += segments[i].getKanji().length();
    }

    return attr;
}

/*!
    \fn HonokaInstance::updateConvertedString()
 */
void HonokaInstance::updateConvertedString()
{
    WideString w = getConvertedText();
    AttributeList a = getConvertedAttributeList();
    int c = 0;
    for(unsigned int i = 0;i < segments.size();i ++) {
        if (m_convertor->getPos() == i) break;
        else c += segments[i].getKanji().length();
    }
    update_preedit_string(w,a);
    update_preedit_caret(c);

}


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



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



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


/*!
    \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]->getName(),
                preeditors[i]->getPropertyName(),String(""),_("mode status"));
            m_proplist.push_back(p);
        }
        if (prediction) {
            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]->getName(),
                    predictors[i]->getPropertyName(),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]->getName(),
                convertors[i]->getPropertyName(),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->getPropertyName() + String(" "));
    }
    update_property(*it);
    if (prediction) {
        it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_PREDICTOR);
        if (it != m_proplist.end()) {
            it->set_label(m_predictor->getPropertyName() + String(" "));
        }
        update_property(*it);
    }
    it = find(m_proplist.begin(),m_proplist.end(),HONOKA_PROP_CONVERTOR);
    if (it != m_proplist.end()) {
        it->set_label(m_convertor->getPropertyName() + String(" "));
    }
    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") + String(" "));
        else if (m_prediction) it->set_label(_("Yosoku") + String(" "));
        else it->set_label(m_preeditor->getModeName() + String(" "));
    }
    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") + String(" "));
        else  it->set_label(_("REN") + String(" "));
    }
    update_property(*it);
    register_properties(m_proplist);
}


/*!
    \fn HonokaInstance::updatePreEditor()
 */
void HonokaInstance::updatePreEditor()
{
    // PreEditorを更新するメソッド。
    // 頻繁に呼ばれます。
    if (PreEditor::getCommitString().length()) {
        commit_string(PreEditor::getCommitString());
        PreEditor::resetCommitString();
    }
    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());
        }
        // Prediction
        if ((!m_conversion) && realtime_prediction && prediction && m_predictor->isConnected() && (!auto_conversion)) {
            if ((predictionDelay > 0) && (preeditCache != m_preeditor->getText())) {
                m_lookup_table.clear();
                m_lookup = false;
                preeditKeyDelay = HonokaTimer::self()->appendDelayEvent(predictionDelay);
                hide_lookup_table();
            } else {
                if (preeditCache != m_preeditor->getText()) {
                    m_convList = m_predictor->getPredictionList(m_preeditor->getText());
                    m_convList.Yomi = 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();
    }
    preeditCache = m_preeditor->getText();
    updateProperty();
}
/*!
    \fn HonokaInstance::updateConversion()
 */
void HonokaInstance::updateConversion()
{
    // 変換時の表示更新。
    updateConvertedString();
    if (m_lookup) {
        m_lookup_table.set_cursor_pos(m_convList.pos);
        update_lookup_table(m_lookup_table);
        update_aux_string(m_convList.Title + getPosPerCount(m_convList.pos,m_convList.count()));
        show_aux_string();
        show_lookup_table();
    } else {
        hide_lookup_table();
        hide_aux_string();
    }
    updateProperty();
}

void HonokaInstance::process_helper_event (const String &helper_uuid, const Transaction &trans) 
{
    WideString t = m_preeditor->getText();
    if (helper_uuid == HONOKA_TIMER_UUID) {
        vector<int> e = HonokaTimer::self()->eventFilter(trans);
        for(unsigned int i = 0;i < e.size();i ++) {
            timerEvent(e[i]);
        }
    }
    if ((!m_conversion) && (!m_prediction) && (t != m_preeditor->getText())) updatePreEditor();
}


bool HonokaInstance::process_key_event (const KeyEvent& key)
{
    // debug
    /*
    String s;
    scim_key_to_string(s,key);
    update_aux_string(utf8_mbstowcs(String(key.is_key_release() ? String("R:") : String("P:")) + s));
    show_aux_string();
    return true;
    */
    
    // キーイベント処理。
    //if (key.is_key_release()) return false;
    /*
    trans.clear();
    trans.put_command(SCIM_TRANS_CMD_REQUEST);
    trans.put_command(SCIM_TRANS_CMD_PROCESS_KEY_EVENT);
    trans.put_data(key);
    send_helper_event("e135e0ee-5588-423e-a027-f07d769c12a3",trans);
    */
    
    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_prediction) return process_prediction_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)
{
    if (!m_preeditor->getTextLength()) pStringType = NORMAL;
    // PreEdit時のキーイベント。
    // PreEditorのキーイベントフック。
    if (m_preeditor->keyEventHook(key)) {
        updatePreEditor();
        return true;
    }
    
    // !! ここでreleaseを除去。
    if (key.is_key_release()) return false;

    // 切り替えキー。
    for(unsigned int i = 0;i < k_preeditor.size();i ++) {
        if (k_preeditor[i].comp(key)) {
            // 同じキーに2種設定があればトグルになると良いと思う。
            if (preeditors[i] != m_preeditor) {
                changePreEditor(preeditors[i]->getName());
                updatePreEditor();
                return true;
            }
        }
    }
    for(unsigned int i = 0;i < k_convertor.size();i ++) {
        if (k_convertor[i].comp(key)) {
            if (convertors[i] == m_convertor) return true;
            changeConvertor(convertors[i]->getName());
            updatePreEditor();
            return true;
        }
    }
    for(unsigned int i = 0;i < k_predictor.size();i ++) {
        if (k_predictor[i].comp(key)) {
            if (predictors[i] == m_predictor) return true;
            //m_predictor = predictors[i];
            changePredictor(predictors[i]->getName());
            updatePreEditor();
            return true;
        }
    }

    // 処理。
    if (k_reconversion.comp(key)) {
        WideString w;
        int c;
        if (get_surrounding_text(w,c)) {
            if (w.length()) {
                // delete_surrounding_text()を使っての削除とかいるね。
                delete_surrounding_text(0 - w.length(),w.length());
                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(getConvertedText());
                if (prediction) if (m_predictor->isConnected()) m_predictor->update(getConvertedText(),m_preeditor->getText(true));
                m_convertor->reset();
            } else {
                commit_string(m_preeditor->getText(true));
                if (prediction) if (m_predictor->isConnected()) m_predictor->update(m_preeditor->getText(true),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();
            if (preeditCache != m_preeditor->getText()) pStringType = NORMAL;
            updatePreEditor();
            return true;
        } else
        if (k_convert_ctype.comp(key)) {
            switch(pStringType) {
                case NORMAL: {
                    pString = m_preeditor->getText();
                    pStringType = HIRA;
                    m_preeditor->kataHira();
                    break;
                }
                case HIRA: {
                    m_preeditor->setText(pString);
                    pStringType = KATA;
                    m_preeditor->hiraKata();
                    break;
                }
                case KATA: {
                    m_preeditor->setText(pString);
                    pStringType = HALF;
                    m_preeditor->toHalf();
                    break;
                }
                case HALF: {
                    m_preeditor->setText(pString);
                    pStringType = WIDE;
                    m_preeditor->toWide();
                    break;
                }
                case WIDE: {
                    m_preeditor->setText(pString);
                    pStringType = HIRA;
                    m_preeditor->kataHira();
                    break;
                }
                default: {
                    break;
                }
            }
            updatePreEditor();
            return true;
        }
        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_prediction.comp(key) && prediction) {
            if ((!realtime_prediction) && m_predictor->isConnected()) {
                m_convList = m_predictor->getPredictionList(m_preeditor->getText());
                m_convList.Yomi = 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();
                }
            }
            if ((m_convList.kType == PREDICTION) && (m_convList.count()) && (m_convList.Yomi == m_preeditor->getText())) {
                return process_prediction_key_event(key);
            }
        } else
        // 数字キーによる直接予測選択。甘いか?。
        if (numkeyselect && realtime_prediction && prediction && select_prediction_direct) {
            if ((m_convList.kType == PREDICTION) && (m_convList.count()) && (m_convList.Yomi == m_preeditor->getText())) {
                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_convList.count() > numc) {
                            m_convList.pos = numc;
                            commit_string(m_convList.kouho.at(m_convList.pos).kanji);
                            m_preeditor->reset();
                            updatePreEditor();
                            return true;
                        }
                    }
                }
            }
        }
    }

    // バッファの存在にかかわらず。
    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]->getName() == m_convertor->getName()) {
                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]->getName());
                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)) {
        if (preeditCache != m_preeditor->getText()) pStringType = NORMAL;
        updatePreEditor();

        return true;
    } else {
        // preeditorで処理できなかった場合はやはりcommitしてアプリケーションに返すべきだ。
        if (m_preeditor->getTextLength()) {
            if (auto_conversion) {
                commit_string(getConvertedText());
                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)
{
    preeditCache.clear();
    // 変換時のキーイベント処理。
    // 喰う!。
    if (key.is_key_release()) return true;
    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_furigana_commit.comp(key)) {
        WideString cm;
        for(unsigned int i = 0;i < segments.size();i ++) {
            if (segments[i].getKanji() == segments[i].getYomi()) {
                cm += segments[i].getKanji();
            } else {
                for(unsigned int j = 0;segments[i].getYomi().length() - j != 0;j ++) {
                    if (segments[i].getKanji()[segments[i].getKanji().length() - j - 1] !=
                        segments[i].getYomi()[segments[i].getYomi().length() - j - 1]) {
                        cm += segments[i].getKanji().substr(0,segments[i].getKanji().length() - j);
                        cm += utf8_mbstowcs(String("("));
                        cm += segments[i].getYomi().substr(0,segments[i].getYomi().length() - j);
                        cm += utf8_mbstowcs(String(")"));
                        cm += segments[i].getKanji().substr(segments[i].getKanji().length() - j,segments[i].getKanji().length());
                        break;
                    }
                }
            }
        }
        commit_string(cm);
        m_convertor->updateFrequency();
        if (prediction) if (m_predictor->isConnected()) m_predictor->update(getConvertedText(),m_preeditor->getText(true));
        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_commit.comp(key)) {
        commit_string(getConvertedText());
        m_convertor->updateFrequency();
        if (prediction) if (m_predictor->isConnected()) m_predictor->update(getConvertedText(),m_preeditor->getText(true));
        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
    
    // 結果をPreEditorに戻すキー。
    if (k_result_to_preedit.comp(key)) {
        preeditStack.push(m_preeditor->getText(true));
        m_preeditor->reset();
        m_preeditor->setText(getConvertedText());
        m_preeditor->setPos(getConvertedText().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 ++;

        if (!m_no_update) m_convertor->select(m_convList.pos);
        updateConversion();
        return true;
    } else
    
    // 注目文節拡縮キー。
    if (k_conversion_expand.comp(key) || k_conversion_shrink.comp(key)) {
        m_no_update = false;
        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
    
    // 連想変換(特殊1)キー
    if (k_conversion_rensou.comp(key)) {
        m_no_update = false;
        m_convList = m_convertor->getResultList(m_convertor->getPos(),SPECIAL1);
        if (m_convList.count() == 0) return true;
        startLookup();
        updateConversion();
        alp_count = 1;
        return true;
    } else
    
    // 異形字変換(特殊2)キー。
    if (k_conversion_ikeiji.comp(key)) {
        m_no_update = false;
        m_convList = m_convertor->getResultList(m_convertor->getPos(),SPECIAL2);
        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)) {
        m_no_update = false;
        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;
        if (k_convert_hiragana.comp(key) && k_convert_katakana.comp(key)) {
            PreEditor::convKataHira(res);
            if (res == m_convList.kouho[m_convList.pos].kanji) PreEditor::convHiraKata(res);
        } else
        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;
                if (!m_no_update) 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]->getName() == m_convertor->getName()) {
                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]->getName());
                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();
                if (!m_no_update) m_convertor->select(m_convList.pos);
                m_no_update = false;
                m_convertor->setPos(m_convertor->getPos() + 1);
                m_convList = m_convertor->getResultList();
                updateConversion();
                startLookup();
                return true;
            }
        }
    }
    
    // 注目文節の候補を別変換エンジンから取得するキー。
    if (m_convertor != m_multi) {
        for(unsigned int i = 0;i < k_convertor.size();i ++) {
            if (k_convertor[i].comp(key)) {
                if (convertors[i] == m_convertor) return true;
                if (!convertors[i]->isConnected())
                    if (!convertors[i]->connect()) return true;
                WideString y;
                for(unsigned int j = 0;j < segments.size();j ++) y += segments[j].getYomi();
                convertors[i]->setYomiText(y);
                //convertors[i]->setYomiText(m_preeditor->getText(true));
                if (convertors[i]->ren_conversion() <= 0) return true;
                m_no_update = true;
                MultiConvertor::aline(m_convertor,convertors[i]);
                m_convList = convertors[i]->getResultList(m_convertor->getPos());
                m_convList.Title = utf8_mbstowcs(String("(") + convertors[i]->getPropertyName() + String(")"));
                convertors[i]->reset();
                startLookup();
                updateConversion();
                return true;
            }
        }
    }
    
    // それ以外のキーの場合、表示可能キーなら確定してPreEdit入力を再開。
    if (key.get_ascii_code()) { // これでは不完全。直せ。
        commit_string(getConvertedText());
        m_convertor->updateFrequency();
        if (prediction) if (m_predictor->isConnected()) m_predictor->update(getConvertedText(),m_preeditor->getText(true));
        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_prediction_key_event(const KeyEvent &key)
 */
bool HonokaInstance::process_prediction_key_event(const KeyEvent &key)
{
    preeditCache.clear();
    // 予測選択時のキーイベント処理。
    if (key.is_key_release()) return true;
    if (!m_prediction) {
        m_prediction = true;
        update_aux_string(m_convList.Title + getPosPerCount(m_convList.pos,m_convList.count()));
        show_aux_string();
        update_preedit_string(m_convList.kouho.at(m_convList.pos).kanji);
        update_preedit_caret(m_predictor->getPos());
        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_prediction.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_aux_string(m_convList.Title + getPosPerCount(m_convList.pos,m_convList.count()));
        show_aux_string();
        update_lookup_table(m_lookup_table);
        update_preedit_string(m_convList.kouho.at(m_convList.pos).kanji);
        update_preedit_caret(m_predictor->getPos());
        updateProperty();
        return true;
    } else
    if (k_commit.comp(key)) {
        m_prediction = 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_prediction = 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);
                m_convList.pos = numc + m_lookup_table.get_current_page_start();
                m_prediction = false;
                commit_string(m_convList.kouho.at(m_convList.pos).kanji);
                m_preeditor->reset();
                updatePreEditor();
                return true;
            }
        }
    }

    if (!key.get_ascii_code()) return true;
    m_prediction = 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;
    if ((!m_no_update) && (m_conversion)) m_convertor->select(m_convList.pos);
    if (m_convList.kType != PREDICTION) {
        updateConvertedString();
    } else {
        update_preedit_string(m_convList.kouho.at(m_convList.pos).kanji);
        update_preedit_caret(0);
        if (!m_prediction) {
            // マウスで選択した場合は予測選択モードに突入。
            m_prediction = true;
            show_preedit_string();
            updateProperty();
        }
    }
    m_lookup_table.set_cursor_pos(m_convList.pos);
    update_aux_string(m_convList.Title + getPosPerCount(m_convList.pos,m_convList.count()));
    show_aux_string();
    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;
    if ((!m_no_update) && (m_conversion)) m_convertor->select(m_convList.pos);
    if (m_conversion) updateConvertedString();
    if (m_prediction) {
        update_preedit_string(m_convList.kouho[m_convList.pos].kanji);
        update_preedit_caret(0);
    }
    m_lookup_table.set_cursor_pos(m_convList.pos);
    update_aux_string(m_convList.Title + getPosPerCount(m_convList.pos,m_convList.count()));
    show_aux_string();
    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;
    if ((!m_no_update) && (m_conversion)) m_convertor->select(m_convList.pos);
    if (m_conversion) updateConvertedString();
    if (m_prediction) {
        update_preedit_string(m_convList.kouho[m_convList.pos].kanji);
        update_preedit_caret(0);
    }
    m_lookup_table.set_cursor_pos(m_convList.pos);
    update_aux_string(m_convList.Title + getPosPerCount(m_convList.pos,m_convList.count()));
    show_aux_string();
    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();
}

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

void HonokaInstance::focus_out ()
{
    // フォーカスを失った時は全てコミット!。
    if (m_conversion) {
        commit_string(getConvertedText());
        m_convertor->updateFrequency();
        if (prediction) if (m_predictor->isConnected()) m_predictor->update(getConvertedText(),m_preeditor->getText(true));
        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_prediction) {
        m_prediction = false;
        commit_string(m_convList.kouho.at(m_convList.pos).kanji);
        m_preeditor->reset();
    }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 server."))));
            show_aux_string();
            m_convertor = m_def_convertor;
            return;
        }
    }

    m_convertor->setYomiText(s);
    if (m_convertor->ren_conversion() <= 0) {
        update_aux_string(utf8_mbstowcs(String(_("The error was received from Converter. "))));
        show_aux_string();
        m_convertor = m_def_convertor;
        return;
    }
    m_no_update = false;
    m_convList.kouho.clear();
    m_convList = m_convertor->getResultList();
    m_conversion = true;
    segments = m_convertor->getSegmentList();

    alp_count = 1;

    show_preedit_string();
    updateConvertedString();
    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 server."))));
            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() <= 0) {
        update_aux_string(utf8_mbstowcs(String(_("could not connect to server."))));
        show_aux_string();
        show_preedit_string();
        update_preedit_string(m_preeditor->getText(),m_preeditor->getAttributeList());
        update_preedit_caret(m_preeditor->getPos());
        return;
    }
    segments = m_convertor->getSegmentList();
    WideString t;
    for(unsigned int i = 0;i < segments.size();i ++) t += segments[i].getKanji();
    show_preedit_string();
    update_preedit_string(t);
    update_preedit_caret(t.length());
}


/*!
    \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_aux_string(cList.Title + getPosPerCount(cList.pos,cList.count()));
    show_aux_string();
    update_lookup_table(m_lookup_table);
}


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


/*!
    \fn HonokaInstance::getPosPerCount(int p,int c)
 */
WideString HonokaInstance::getPosPerCount(int p,int c)
{
    char data[256];
    sprintf(data," [%d/%d]",p + 1,c);
    return utf8_mbstowcs(String(data));
}









/*!
    \fn HonokaInstance::timerEvent(int id)
 */
void HonokaInstance::timerEvent(int id)
{
    for(unsigned int i = 0;i < preeditors.size();i ++) {
        if (preeditors[i]->findTimerEventId(id)) {
            preeditors[i]->timerEvent(id);
            //if ((!m_conversion) && (!m_prediction)) updatePreEditor();
            return;
        }
    }
    for(unsigned int i = 0;i < convertors.size();i ++) {
        if (convertors[i]->findTimerEventId(id)) {
            convertors[i]->timerEvent(id);
            return;
        }
    }
    for(unsigned int i = 0;i < predictors.size();i ++) {
        if (predictors[i]->findTimerEventId(id)) {
            predictors[i]->timerEvent(id);
            return;
        }
    }
    if ((id == preeditKeyDelay) && (!m_conversion) && (!m_prediction)) {
        WideString w = m_preeditor->getText();
        if (!w.length()) {
            hide_lookup_table();
            return;
        }
        m_convList = m_predictor->getPredictionList(w);
        m_convList.Yomi = w;
        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);
            }
            if (w == m_preeditor->getText()) {
                startLookup();
                hide_aux_string();
            }
            else hide_lookup_table();
        } else {
            //m_lookup_table.clear();
            hide_lookup_table();
        }
    }
}