// XIMプロトコルのパケットでIC(Input Context)に関わる物を処理する
// 特にキーが押されたというメッセージと文字をコミットするメッセージを
// 仮名漢字モジュールへとの間で仲介する。
 
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
 
#include <stdlib.h>
#include <ctype.h>
#include "xim.h"
#include "convdisp.h"
#include "kkconv.h"
 
#ifndef __GNUC__
# ifdef HAVE_ALLOCA_H
#  include <alloca.h>
# endif
#endif

extern input_style input_style_tab[];

char invalid_style_msg[]=
"サポートしていない入力スタイルの\n"
"設定をクライアントが要求してきました。";

XimIC *XimIC::current_ic;
int XimIC::nrActiveIC;

pe_stat::pe_stat(KKContext *c)
{
    cont = c;
    clear();
}

void pe_stat::clear()
{
    jstrings.erase(jstrings.begin(), jstrings.end());
    cands = 0;
}

void pe_stat::new_segment(int s)
{
    pe_jstring p;
    p.stat = s;
    jstrings.push_back(p);
}

void pe_stat::push_cchar(cchar c)
{
    std::list<pe_jstring>::reverse_iterator i= jstrings.rbegin();
    (*i).s.push_back(c);
}

int pe_stat::get_char_count()
{
    std::list<pe_jstring>::iterator i;
    jstring_t::iterator j;
    int k=0;
    for (i = jstrings.begin(); i != jstrings.end(); i++) {
	for (j = (*i).s.begin(); j != (*i).s.end(); j++) {
	    k++;
	}
    }
    return k;
}

icxatr::icxatr()
{
    atr_mask = 0;
    change_mask = 0;
    font_set_name = NULL;
    font_set = NULL;
    foreground_pixel = BlackPixel(gDpy, DefaultScreen(gDpy));
    background_pixel = WhitePixel(gDpy, DefaultScreen(gDpy));
}

icxatr::~icxatr()
{
    if (font_set_name) {
	free((void *)font_set_name);
	XFreeFontSet(gDpy,font_set);
    }
}

bool icxatr::has_atr(int id)
{
    return atr_mask & (1<<id);
}

void icxatr::set_atr(int id, C8 *val, int len, int o)
{
    switch (id) {
    case ICA_InputStyle:
	input_style = readC32(val,o);
	break;
    case ICA_ClientWindow:
	client_window = readC32(val,o);
	break;
    case ICA_FocusWindow:
	focus_window = readC32(val,o);
	break;
    case ICA_Foreground:
	foreground_pixel = readC32(val,o);
	break;
    case ICA_Background:
	background_pixel = readC32(val,o);
	break;
    case ICA_SpotLocation:
	spot_location.x = readC16(val,o);
	spot_location.y = readC16(&val[2],o);
	break;
    case ICA_FontSet:
    {
	int len = readC16(val,o);
	char *new_fsn;
	new_fsn = (char *)alloca(len+1);
	new_fsn[len] = 0;
	memcpy(new_fsn,&val[2],len);

	if (font_set_name && !strcmp(new_fsn, font_set_name)) {
	    break;
	}

	if (font_set_name) {
	    free(font_set_name);
	}
	font_set_name = strdup(new_fsn);

	char **missing,*def;
	int nr_missing;
	font_set = XCreateFontSet(gDpy,font_set_name,
				  &missing,&nr_missing,&def);
	if (missing) {
	    XFreeStringList(missing);
	}
    }
    break;
    case ICA_Area:
    {
	area.x = readC16(&val[0],o);
	area.y = readC16(&val[2],o);
	area.width = readC16(&val[4],o);
	area.height = readC16(&val[6],o);              
    }
    break;
    case ICA_LineSpace:
	line_space=readC16(val,o);
	break;
    default:
	//未知のアトリビュート
	printf("try to set unknown ic attribute %d.\n",id);
	return ;
    }
    atr_mask |= (1 << id);
    change_mask |= (1 << id);
}

bool icxatr::is_changed(int id)
{
    if (change_mask & (1<<id)) {
	return true;
    }
    return false;
}

void icxatr::unset_change_mask(int id)
{
    change_mask &=(~(1<<id));
}

void icxatr::print()
{
    if (has_atr(ICA_InputStyle)) {
        printf("input-style %ld.\n", input_style);
    } else {
        printf("input-style undefined.\n");
    }
    if (has_atr(ICA_ClientWindow)) {
        printf("client-window id %ld.\n", client_window);
    } else {
        printf("client-window id undefined.\n");
    }
    if (has_atr(ICA_FocusWindow)) {
        printf("focus-window id %ld.\n", focus_window);
    }else{
        printf("focus-window id undefined.\n");
    }
    /*Preedit Attributesは具体的な値を持ちません。*/
    if (has_atr(ICA_Foreground)) {
        printf("foreground-pixel %d.\n", foreground_pixel);
    } else {
        printf("foreground-pixel undefined.\n");
    }
    if (has_atr(ICA_Background)) {
        printf("background-pixel %d.\n", background_pixel);
    } else {
        printf("background-pixel undefined.\n");
    }
    if (has_atr(ICA_SpotLocation)) {
        printf("spot location x=%d,y=%d.\n",
	       spot_location.x, spot_location.y);
    } else {
        printf("spot location undefined.\n");
    }
    if (has_atr(ICA_FontSet)) {
        printf("font-set-name %s\n", font_set_name);
    } else {
        printf("font-set-name undefined.\n");
    }
    if (has_atr(ICA_Area)) {
        printf("area = x=%d y=%d width=%d height=%d.\n",
	       area.x, area.y, area.width, area.height);
    } else {
        printf("area undefined.\n");
    }
    if (has_atr(ICA_LineSpace)) {
        printf("line-space %d.\n", line_space);
    } else {
        printf("line-space undefined.\n");
    }
}

int icxatr::getSize(int id)
{
    switch (id) {
    case ICA_FocusWindow:
	return 4;
    case ICA_FilterEvents:
	return 4;
    }
    return 0;
}

XimIC::XimIC(Connection *c, int imid, int icid)
{
    m_conn = c;
    mIMid = imid;
    mICid = icid;
    mIsActive = false;
    m_kkContext = createKKContext(this);
    mConvdisp = 0;
    if (g_option_mask & OPT_TRACE) {
	printf("imid=%d, icid=%d ic created.\n", mIMid, mICid);
    }
}

XimIC::~XimIC()
{
    if (g_option_mask & OPT_TRACE) {
	printf("imid=%d, icid=%d ic deleted.\n",mIMid ,mICid);
    }
    unset_focus();
    if (current_ic == this) {
	current_ic = 0;
    }

    //この順序は重要
    delete m_kkContext;
    if (mConvdisp) {
	delete mConvdisp;
    }
}

bool XimIC::isActive()
{
    return mIsActive;
}

int XimIC::get_icid()
{
    return mICid;
}

int XimIC::get_imid()
{
    return mIMid;
}

void XimIC::set_focus()
{
    if (mIsActive) {
	return ;
    }
    current_ic = this;
    mIsActive = true;
    nrActiveIC ++;
    ui_update_ic_stat();
    ui_update_input_mode(m_kkContext->getMode());
    if (mConvdisp) {
	mConvdisp->set_focus();
    }
}

void XimIC::unset_focus()
{
    if (!mIsActive) {
	return ;
    }
    mIsActive = false;
    nrActiveIC --;
    if (current_ic == this) {
	ui_update_ic_stat();
    }

    if (mConvdisp) {
	mConvdisp->unset_focus();
    }
}

void XimIC::OnKeyEvent(keyEventX e)
{

    int s,t = 0;
    keyState k(&e);

    if (k.is_modifier()) {
	return ;
    }
    jstring_t *c= new jstring_t();

    do {
	s = m_kkContext->pushKey(&k);
	t |= s;

	if (s & COMMIT_RAW) {
	    send_key_event(&e.ev.xkey);
	}

	if (s & COMMIT_WIDE) {
	    cchar ac;
	    ac = ascii_to_wide(k.char_code());
	    if (ac) {
		c->push_back(ac);
	    } else {
		send_key_event(&e.ev.xkey);
	    }
	}
  
    } while(s & PUSHKEY_AGAIN);

    if (t & UPDATE_MODE) {
	int mode = m_kkContext->getMode();
	ui_update_input_mode(mode);
    }

    if (t & COMMIT_WIDE) {
	commit_jstring(c);
    }
    delete c;
}

void XimIC::changeMode(int s)
{
    m_kkContext->setMode(s);
    
    ui_update_input_mode(m_kkContext->getMode());
}

void XimIC::send_key_event(XKeyEvent *e)
{
    TxPacket *t;
    t = createTxPacket(XIM_FORWARD_EVENT,0);
    t->pushC16(mIMid);
    t->pushC16(mICid);
    t->pushC16(1); //flag , synchronous
    t->pushC16((e->serial>>16)&0xffff);

    t->pushC8(e->type);
    t->pushC8(e->keycode);
    t->pushC16(e->serial &0xffff);
    t->pushC32(e->time);
    t->pushC32(e->root);
    t->pushC32(e->window);
    t->pushC32(e->subwindow);
    t->pushC16(e->x_root);
    t->pushC16(e->y_root);
    t->pushC16(e->x);
    t->pushC16(e->y);
    t->pushC16(e->state);
    t->pushC8(e->same_screen);
    t->pushC8(0);
    m_conn->push_packet(t);
}

void XimIC::commit_jstring(jstring_t *s)
{
    append_jstring(&mPending, s);
}

void XimIC::extra_input(jstring_t *s)
{
    if (!m_kkContext->extra_input(s)) {
        commit_jstring(s);
    }
    if (m_xatr.has_atr(ICA_FocusWindow)) {
        force_event(m_xatr.focus_window);
    }
}

void XimIC::set_ic_attrs(void *val, int len)
{
    unsigned char *p=(unsigned char *)val;
    int byte_order = m_conn->byte_order();
    int i;
    for (i = 0; i < len ;) {
	int atr_id, atr_len;
	atr_id = readC16(&p[i], byte_order);
	i += 2;

	atr_len = readC16(&p[i], byte_order);
	i += 2;

	unsigned char *q;
	q = (unsigned char *)alloca(atr_len+pad4(atr_len));

	int j;
	for(j = 0; j < atr_len +pad4(atr_len); j++, i++) {
	    q[j] = p[i];
	}

	set_ic_attr(atr_id, (C8 *)q, atr_len);
    }
}

int XimIC::get_ic_atr(int id, TxPacket *t)
{
    int l = m_xatr.getSize(id);
    if (!t) {
	return l;
    }
    switch (id) {
    case ICA_FocusWindow:
	t->pushC32(m_xatr.focus_window);
	break;
    case ICA_FilterEvents:
	t->pushC32(KeyPressMask);
	break;
    default:
	printf("try to get unknown ic attribute %d.\n",id);
	break;
    }
    return l;
}

int XimIC::lookup_style(unsigned long s)
{
    int i;
    for (i = 0; input_style_tab[i].x_style; i++) {
        if (input_style_tab[i].x_style == (int)s) {
            return input_style_tab[i].style;
        }
    }
    return IS_INVALID;
}

void XimIC::set_ic_attr(int id,C8 *val, int len)
{
    if (id == ICA_PreeditAttribute || id == ICA_StatusAttributes) {
        // attributeリスト
	set_ic_attrs(val, len);
    } else {
	m_xatr.set_atr(id, val, len, m_conn->byte_order());
    }
    if (mConvdisp) {
	mConvdisp->update_icxatr();
    }
    if (!mConvdisp && m_xatr.has_atr(ICA_InputStyle)) {
	mConvdisp = create_convdisp(
	    lookup_style(m_xatr.input_style), m_kkContext, &m_xatr, m_conn);
	if (!mConvdisp && id == ICA_InputStyle) {
	    show_issue(invalid_style_msg);
	}
    }
    m_kkContext->set_convdisp(mConvdisp);
    if (mConvdisp) {
	mConvdisp->update_icxatr();
    }
}

void XimIC::reset_ic()
{
    TxPacket *t;
    t = createTxPacket(XIM_RESET_IC_REPLY,0);
    t->pushC16(mIMid);
    t->pushC16(mICid);

    jstring_t *s;
    s = m_kkContext->clear();
    if (s) {
        char *p;
        int i,len;
        p = jstring_to_ctext(s);
        len = strlen(p);
        t->pushC16(len);
        for (i = 0 ; i < len; i++) {
            t->pushC8(p[i]);
        }
        len = pad4(len+2);
        for (i = 0; i < len; i++) {
            t->pushC8(p[i]);
        }
        free(p);
        delete s;
    }else{
        t->pushC16(0);//コミットする文字の長さ
        // ここでコミットする文字を入れる。
        t->pushC16(0);//padding ( 2bytes)
    }

    m_conn->push_packet(t);
}

Convdisp *XimIC::get_convdisp()
{
    return mConvdisp;
}

void XimIC::onSendPacket()
{
    if (!mPending.size()) {
        return;
    }
    char *p;
    p = jstring_to_ctext(&mPending);

    TxPacket *t;
    t = createTxPacket(XIM_COMMIT, 0);
    t->pushC16(mIMid);
    t->pushC16(mICid);

    t->pushC16(3); //XLookupChars|同期

    int i,len;
    len = strlen(p);

    t->pushC16(len);

    for (i = 0 ; i < len ; i++) {
	t->pushC8(p[i]);
    }
    len = pad4(len);
    for (i = 0 ; i < len ; i++) {
	t->pushC8(0);
    }
    free(p);

    m_conn->push_passive_packet(t);

    erase_jstring(&mPending);
}

XimIC *XimIC::get_current_ic()
{
    return current_ic;
}

bool XimIC::isAnyActive()
{
    if ( nrActiveIC ){
	return true;
    }
    return false;
}

XimIC *create_ic(Connection *c,RxPacket *p,int imid,int icid)
{
    XimIC *ic;
    ic = new XimIC(c,imid,icid);
    p->rewind();
    p->getC16();//discard
    int atr_len = p->getC16();
    unsigned char *v;
    v = (unsigned char *)alloca(atr_len);
    int i;
    for ( i = 0 ; i < atr_len ;i++){
	v[i] = p->getC8();
    }
    ic->set_ic_attrs((void *)v,atr_len);
    if ( !ic->get_convdisp()){
	delete ic;
	return 0;
    }
    return ic;
}
/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1