/***************************************************************************
* 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. *
***************************************************************************/
// って言うか先にWnnをC++向けに優しく包み込む母のようなライブラリ激しく希望。
// ああそうですね、「お前がやれ」ですよね。
#define Uses_SCIM_UTILITY
#define Uses_SCIM_IMENGINE
#define Uses_SCIM_LOOKUP_TABLE
#define Uses_SCIM_CONFIG_BASE
#define Uses_SCIM_ICONV
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
// 国際化のおまじない。
#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 "scim_wnn_imengine.h"
#include "scim_wnn_def.h"
#define scim_module_init wnn_LTX_scim_module_init
#define scim_module_exit wnn_LTX_scim_module_exit
#define scim_imengine_module_init wnn_LTX_scim_imengine_module_init
#define scim_imengine_module_create_factory wnn_LTX_scim_imengine_module_create_factory
#ifndef SCIM_WNN_ICON_FILE
#define SCIM_WNN_ICON_FILE (SCIM_ICONDIR "/scim-wnn.png")
#endif
static Pointer <WnnFactory> _scim_wnn_factory;
static ConfigPointer _scim_config;
extern "C" {
void scim_module_init (void)
{
bindtextdomain (GETTEXT_PACKAGE, SCIM_WNN_LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
}
void scim_module_exit (void)
{
_scim_wnn_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 (_scim_wnn_factory.null ()) {
_scim_wnn_factory =
new WnnFactory (utf8_mbstowcs (String (_("Wnn"))),String("ja_JP"));
}
return _scim_wnn_factory;
}
}
WnnFactory::WnnFactory() {
m_name = utf8_mbstowcs(_("Wnn"));
set_languages(String("ja_JP"));
}
WnnFactory::~ WnnFactory() {
}
WnnFactory::WnnFactory(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 WnnFactory::get_name () const
{
return m_name;
}
WideString WnnFactory::get_authors () const
{
return utf8_mbstowcs (String (_("(C) 2004 TAM(Teppei Tamra) <tam-t@par.odn.ne.jp>")));
}
WideString WnnFactory::get_credits () const
{
return WideString ();
}
WideString WnnFactory::get_help () const
{
return utf8_mbstowcs (String (_("SCIM-WNN HELP")));
}
String WnnFactory::get_uuid () const
{
return String ("c069395a-d62f-4a77-8229-446e44a99976");
}
String WnnFactory::get_icon_file () const
{
return String (SCIM_WNN_ICON_FILE);
}
IMEngineInstancePointer WnnFactory::create_instance (const String& encoding, int id)
{
return new WnnInstance (this, encoding, id);
}
// キーイベントチェック
void WnnKeyEventList::append(const KeyEvent &k) {
push_back(k);
}
void WnnKeyEventList::append(int code,int mask) {
KeyEvent k;
k.code = code;
k.mask = mask;
append(k);
}
bool WnnKeyEventList::comp(const KeyEvent &k) {
for (unsigned int i = 0;i < size();i ++) {
if ((at(i).code == k.code) && (at(i).mask == k.mask)) {
return(true);
}
}
return(false);
}
// 現物。
WnnInstance::WnnInstance (WnnFactory *factory, const String& encoding, int id)
: IMEngineInstanceBase (factory, encoding, id)
{
m_iconv.set_encoding ("EUC-JP");
m_connected = false;
preeditors.push_back(new Romkan(_scim_config));
preeditors.push_back(new KanaInput(_scim_config));
m_preeditor = preeditors.at(0);
init();
}
WnnInstance::~WnnInstance()
{
for(unsigned int i = 0;i < preeditors.size();i ++) {
delete(preeditors.at(i));
}
}
/*!
\fn WnnInstance::init()
*/
void WnnInstance::init()
{
m_conversion = false;
m_lookup = false;
// @todo if connected to jserver, should disconnect this.
host = _scim_config->read(String(SCIM_CONFIG_WNN_SERVER),String(SCIM_DEFAULT_WNN_SERVER));
rc = _scim_config->read(String(SCIM_CONFIG_WNN_RC),String(SCIM_DEFAULT_WNN_RC));
alp = _scim_config->read(String(SCIM_CONFIG_WNN_ALP),SCIM_DEFAULT_WNN_ALP);
mini_status = _scim_config->read(String(SCIM_CONFIG_WNN_MINISTATUS),SCIM_DEFAULT_WNN_MINISTATUS);
// デフォルトキー設定。
scim_string_to_key_list(k_conversion_start,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_START),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_START)));
scim_string_to_key_list(k_cancel,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CANCEL),
String(SCIM_DEFAULT_WNN_KEY_CANCEL)));
scim_string_to_key_list(k_delete,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_DELETE),
String(SCIM_DEFAULT_WNN_KEY_DELETE)));
scim_string_to_key_list(k_backspace,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_BACKSPACE),
String(SCIM_DEFAULT_WNN_KEY_BACKSPACE)));
scim_string_to_key_list(k_commit,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_COMMIT),
String(SCIM_DEFAULT_WNN_KEY_COMMIT)));
scim_string_to_key_list(k_conversion_next,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_NEXT),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_NEXT)));
scim_string_to_key_list(k_conversion_prev,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_PREV),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_PREV)));
scim_string_to_key_list(k_conversion_expand,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_EXPAND),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_EXPAND)));
scim_string_to_key_list(k_conversion_shrink,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_SHRINK),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_SHRINK)));
scim_string_to_key_list(k_conversion_forward,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_FORWARD),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_FORWARD)));
scim_string_to_key_list(k_conversion_backward,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_BACKWARD),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_BACKWARD)));
scim_string_to_key_list(k_forward,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_FORWARD),
String(SCIM_DEFAULT_WNN_KEY_FORWARD)));
scim_string_to_key_list(k_backward,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_BACKWARD),
String(SCIM_DEFAULT_WNN_KEY_BACKWARD)));
scim_string_to_key_list(k_lookup_popup,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_LOOKUPPOPUP),
String(SCIM_DEFAULT_WNN_KEY_LOOKUPPOPUP)));
scim_string_to_key_list(k_lookup_pageup,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_LOOKUPPAGEUP),
String(SCIM_DEFAULT_WNN_KEY_LOOKUPPAGEUP)));
scim_string_to_key_list(k_lookup_pagedown,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_LOOKUPPAGEDOWN),
String(SCIM_DEFAULT_WNN_KEY_LOOKUPPAGEDOWN)));
scim_string_to_key_list(k_convert_hiragana,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERT_HIRAGANA),
String(SCIM_DEFAULT_WNN_KEY_CONVERT_HIRAGANA)));
scim_string_to_key_list(k_convert_katakana,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERT_KATAKANA),
String(SCIM_DEFAULT_WNN_KEY_CONVERT_KATAKANA)));
scim_string_to_key_list(k_conversion_rensou,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_RENSOU),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_RENSOU)));
scim_string_to_key_list(k_conversion_ikeiji,
_scim_config->read(String(SCIM_CONFIG_WNN_KEY_CONVERSION_IKEIJI),
String(SCIM_DEFAULT_WNN_KEY_CONVERSION_IKEIJI)));
}
/*!
\fn WnnInstance::changePreEditor(const QString &name)
*/
void WnnInstance::changePreEditor(const String &name)
{
for(unsigned int i = 0;i < preeditors.size();i ++) {
if (preeditors[i]->getPropertyName() == name) {
m_preeditor = preeditors[i];
return;
}
}
return;
}
/*!
\fn WnnInstance::updateProperty()
*/
void WnnInstance::updateProperty()
{
if (m_proplist.empty()) {
Property p;
p = Property(SCIM_PROP_WNN_INPUTMODE,"",String(""),_("input mode"));
m_proplist.push_back(p);
for(unsigned int i = 0;i < preeditors.size();i ++) {
p = Property(String(SCIM_PROP_WNN_INPUTMODE) + String("/") + preeditors[i]->getPropertyName(),
preeditors[i]->getName(),String(""),_("mode status"));
m_proplist.push_back(p);
}
p = Property(SCIM_PROP_WNN_MODESTATUS,"",String(""),_("mode status"));
m_proplist.push_back(p);
}
PropertyList::iterator it;
it = find(m_proplist.begin(),m_proplist.end(),SCIM_PROP_WNN_INPUTMODE);
if (it != m_proplist.end()) {
it->set_label(m_preeditor->getName());
}
update_property(*it);
it = find(m_proplist.begin(),m_proplist.end(),SCIM_PROP_WNN_MODESTATUS);
if (it != m_proplist.end()) {
if (m_conversion) it->set_label(_("Kanji"));
else it->set_label(m_preeditor->getModeName());
}
update_property(*it);
register_properties(m_proplist);
}
/*!
\fn WnnInstance::updatePreEditor()
*/
void WnnInstance::updatePreEditor()
{
if (m_preeditor->getTextLength()) {
show_preedit_string();
update_preedit_string(m_preeditor->getText());
update_preedit_caret(m_preeditor->getPos());
} else {
hide_preedit_string();
}
if (mini_status) {
update_aux_string(utf8_mbstowcs(m_preeditor->getModeName()));
show_aux_string();
} else {
hide_aux_string();
}
hide_lookup_table();
updateProperty();
}
/*!
\fn WnnInstance::updateConversion()
*/
void WnnInstance::updateConversion()
{
update_preedit_string(wnn.getText(),wnn.getAttributeList());
update_preedit_caret(wnn.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 WnnInstance::process_key_event (const KeyEvent& key)
{
if (key.is_key_release()) return false;
if (m_conversion) return(process_conversion_key_event(key));
else return(process_preedit_key_event(key));
}
/*!
\fn WnnInstance::process_preedit_key_event(const KeyEvent &key)
*/
bool WnnInstance::process_preedit_key_event(const KeyEvent &key)
{
// PreEditorのキーイベントフック。
if (m_preeditor->keyEventHook(key)) {
updatePreEditor();
return(true);
} else
// バッファが空では無い場合。
if (m_preeditor->getTextLength()) {
if (k_conversion_start.comp(key)) {
startConversion(m_preeditor->getText(true));
alp_count ++;
return(true);
} else
if (k_commit.comp(key)) {
commit_string(m_preeditor->getText(true));
m_preeditor->reset();
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_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);
}
}
// バッファの存在にかかわらず。
if (k_cancel.comp(key)) {
if (m_preeditor->cancelEvent()) {
updatePreEditor();
return(true);
}
if (m_preeditor->getTextLength()) {
m_preeditor->reset();
}
updatePreEditor();
return(true);
}
if (m_preeditor->inputEvent(key)) {
updatePreEditor();
return(true);
} else {
// preeditorで処理できなかった場合はやはりcommitしてアプリケーションに返すべきだ。
if (m_preeditor->getTextLength()) commit_string(m_preeditor->getText(true));
m_preeditor->reset();
updatePreEditor();
}
return(false);
}
/*!
\fn WnnInstance::process_conversion_key_event(const KeyEvent &key)
*/
bool WnnInstance::process_conversion_key_event(const KeyEvent &key)
{
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(wnn.getText());
wnn.updateFrequency();
m_preeditor->reset();
wnn.reset();
m_conversion = false;
updatePreEditor();
m_lookup = false;
alp_count = 1;
return(true);
} else
if (k_cancel.comp(key) || k_backspace.comp(key)) {
wnn.reset();
m_conversion = false;
m_lookup = false;
alp_count = 0;
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 ++;
wnn.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 = wnn.resizeRegion(-1) : r = wnn.resizeRegion(1);
if (!r) return(true);
m_convList = wnn.getConversionList();
if (alp == -1) {
startLookup();
} else m_lookup = false;
updateConversion();
alp_count = 1;
return(true);
} else
if (k_conversion_rensou.comp(key)) {
m_convList = wnn.getConversionList(wnn.getPos(),RENSOU);
startLookup();
updateConversion();
alp_count = 1;
return(true);
} else
if (k_conversion_ikeiji.comp(key)) {
m_convList = wnn.getConversionList(wnn.getPos(),IKEIJI);
startLookup();
updateConversion();
alp_count = 1;
return(true);
} else
if (k_conversion_forward.comp(key) || k_conversion_backward.comp(key)) {
k_conversion_backward.comp(key) ? wnn.setPos(wnn.getPos() - 1) : wnn.setPos(wnn.getPos() + 1);
m_convList = wnn.getConversionList();
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]) {
m_convList.pos = i;
wnn.select(m_convList.pos);
updateConversion();
break;
}
}
return(true);
} else
if (isprint(key.code)) {
if (key.mask & (SCIM_KEY_AltMask | SCIM_KEY_ControlMask)) return(false);
if (m_lookup) {
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();
wnn.select(m_convList.pos);
wnn.setPos(wnn.getPos() + 1);
m_convList = wnn.getConversionList();
updateConversion();
startLookup();
return(true);
}
}
commit_string(wnn.getText());
wnn.updateFrequency();
m_preeditor->reset();
wnn.reset();
m_conversion = false;
m_lookup = false;
alp_count = 0;
updatePreEditor();
return(process_preedit_key_event(key));
}
return(true);
}
void WnnInstance::move_preedit_caret (unsigned int pos)
{
//if (!m_conversion) m_preeditor->setPos(pos);
//update_preedit_caret(pos);
}
void WnnInstance::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;
wnn.select(m_convList.pos);
update_preedit_string(wnn.getText(),wnn.getAttributeList());
update_preedit_caret(wnn.getCaretPos());
m_lookup_table.set_cursor_pos(m_convList.pos);
update_lookup_table(m_lookup_table);
}
void WnnInstance::update_lookup_table_page_size (unsigned int page_size)
{
m_lookup_table.set_page_size (page_size);
}
void WnnInstance::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;
wnn.select(m_convList.pos);
update_preedit_string(wnn.getText(),wnn.getAttributeList());
update_preedit_caret(wnn.getCaretPos());
m_lookup_table.set_cursor_pos(m_convList.pos);
update_lookup_table(m_lookup_table);
}
void WnnInstance::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;
wnn.select(m_convList.pos);
update_preedit_string(wnn.getText(),wnn.getAttributeList());
update_preedit_caret(wnn.getCaretPos());
m_lookup_table.set_cursor_pos(m_convList.pos);
update_lookup_table(m_lookup_table);
}
void WnnInstance::reset ()
{
alp_count = 0;
m_conversion = false;
m_lookup = false;
wnn.reset();
m_preeditor->reset();
updatePreEditor();
}
void WnnInstance::focus_in ()
{
if (!m_conversion) updatePreEditor();
updateProperty();
}
void WnnInstance::focus_out ()
{
if (m_conversion) {
commit_string(wnn.getText());
wnn.updateFrequency();
wnn.reset();
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 WnnInstance::trigger_property (const String &property)
{
String s = SCIM_PROP_WNN_INPUTMODE;
if ((property.length() > s.length()) && (property.substr(0,s.length()) == s)) {
changePreEditor(property.substr(s.length() + 1));
updateProperty();
}
}
/*!
\fn WnnInstance::startConversion(WideString s)
*/
void WnnInstance::startConversion(WideString s)
{
hide_aux_string();
if (!m_connected) {
m_connected = wnn.wnnConnect(String("scim-wnn"),host,rc,0);
}
if (!m_connected) {
update_aux_string(utf8_mbstowcs(String(_("could not connect to jserver."))));
show_aux_string();
return;
}
wnn.setYomiText(s);
if (wnn.ren_conversion() == -1) {
update_aux_string(utf8_mbstowcs(String(_("could not connect to jserver."))));
show_aux_string();
return;
}
m_convList = wnn.getConversionList();
m_conversion = true;
alp_count = 1;
show_preedit_string();
update_preedit_string(wnn.getText(),wnn.getAttributeList());
update_preedit_caret(wnn.getCaretPos());
if (alp == -1) {
if (!m_lookup) startLookup();
}
updateProperty();
}
/*!
\fn WnnInstance::createLookupTable(WnnConversionList cList)
*/
void WnnInstance::createLookupTable(WnnConversionList cList)
{
m_lookup_table.clear();
if (!cList.count) return;
for (unsigned int i = 0;i < cList.count;i ++) {
m_lookup_table.append_candidate(cList.kouho.at(i));
}
m_lookup_table.set_cursor_pos(cList.pos);
update_lookup_table(m_lookup_table);
}
/*!
\fn WnnInstance::startLookup()
*/
void WnnInstance::startLookup()
{
createLookupTable(m_convList);
m_lookup = true;
switch(m_convList.kType) {
case IKEIJI: {
update_aux_string(utf8_mbstowcs(String(_("Ikeiji lookup result"))));
break;
}
case RENSOU: {
update_aux_string(utf8_mbstowcs(String(_("association lookup result"))));
break;
}
default: {
update_aux_string(utf8_mbstowcs(String(_("lookup result"))));
break;
}
}
show_aux_string();
show_lookup_table();
}