// classes for preedit draw
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>
#include <stdlib.h>
#include "xim.h"
#include "kkconv.h"
#include "convdisp.h"
#include "xdispatch.h"
#include "gtkdispatch.h"

#define INVALID_POS -1

//
// PeWinプリエディット用の基本ウィンドウ
//  PeLineWin  root window style
//  PeOvWin  over the spot
// CandidateWin 候補表示用のウィンドウ

//IMEはインタラクティブである必要があるので、気安くXFlushを使う。

static XFontSet gFontset;

void init_convdisp()
{
    char **missing, *def;
    int nr_missing;
    gFontset = 
	XCreateFontSet(gDpy,
		       "-misc-fixed-medium-r-normal--14-110-100-100-c-70-iso8859-1,"
		       "-misc-fixed-medium-r-normal--14-130-75-75-c-140-jisx0208.1983-0,"
		       "-misc-fixed-medium-r-normal--14-130-75-75-c-70-jisx0201.1976-0",
		       &missing, &nr_missing, &def);
    // ktermの使うフォントセットを取ってきた。

    if (missing) {
	XFreeStringList(missing);
    }
}

struct char_ent {
    cchar c;
    int stat;
    int width;
    int height;
    int x, y;
};

// プリエディットWindow
class PeWin : public WindowIf {
public:
    PeWin(Window pw);
    virtual ~PeWin();

    virtual void release();
    
    virtual void destroy(Window);
    void expose(Window w);

    void draw_char(int x,int y,cchar ch,int stat);
    void set_back(unsigned long p);
    void set_fore(unsigned long p);
    void set_fontset(XFontSet f);
    
    virtual void set_size(int w,int h);
    void set_pos(int x,int y);

    void do_map();

    void clear();
    void draw();
    void unmap();
protected:
    Window mParentWin;
    Window mWin;
    Pixmap mPixmap;
    GC mGC,mClearGC;
    GC mHilitGC;
    unsigned int mFore,mBack;
    XFontSet mFontset;
    int mWidth,mHeight;
    bool mIsMapped;
};

// RootWindowStyle用の1行ウィンドウ
class PeLineWin : public PeWin {
public:
    PeLineWin(Window w);
    virtual ~PeLineWin();

    void draw_pe(pe_stat *p);

    void draw_segment(pe_jstring *s);

private:
    void calc_extent(pe_stat *p);

    int m_x;
};

// over the spot用のウィンドウ
class PeOvWin : public PeWin {
public:
    PeOvWin(Window w);
    
    void draw_ce(char_ent *ce,int len);
    virtual void set_size(int w,int h);
    virtual ~PeOvWin();
private:
    void draw_a_ce(char_ent *ce);
    Pixmap m_mask_pix;
    GC m_mask_pix_gc;
};

//
//候補表示用のウィンドウ
// ConvdispOvの作成時と破棄時にそれぞれ作成、破棄される
//
class CandidateWin : WidgetIf{
public:
    CandidateWin(Convdisp *);
    virtual ~CandidateWin();
    void update(pe_stat *);
    virtual void select_row(GtkWidget *,int,int );
    virtual void key_press(GtkWidget *w,GdkEventKey *e);

    void set_pos(int x, int y);

    void show();
    void hide();
private:
    void build_candidate_list();
    bool trim_base_row();

    GtkWidget *top_win;
    GtkWidget *clist;
    Convdisp *mConvdisp;
    pe_stat *m_pe;
    bool bInit;
    int m_select_count;
    int m_base_row;
};

class ConvdispOv : public Convdisp{
public:
    ConvdispOv(KKContext *,icxatr *);
    virtual ~ConvdispOv();
    void update_preedit();
    virtual void update_icxatr();
    virtual void set_focus();
    virtual void unset_focus();
private:
    bool check_win();
    bool check_atr();
    void layoutCharEnt();
    void make_ce_array();
    void drawPreedit();
    void do_draw_preedit();
    int calc_ce_width(int b,int e);

    void validate_area();
    
    char_ent *m_ce;
    int m_ce_len;
    PeOvWin *m_ov_win;
};

// RootWindowスタイル
class ConvdispRw : public Convdisp{
public:
    ConvdispRw(KKContext *,icxatr *);
    virtual ~ConvdispRw();

    void update_preedit();
    virtual void update_icxatr();
private:
    PeLineWin *mPeWin;
};

class Connection;
class ConvdispOs : public Convdisp{
public:
    ConvdispOs(KKContext *,icxatr *,Connection *);
    virtual ~ConvdispOs();
    virtual void update_preedit();
    virtual void update_icxatr();
    virtual void set_focus();
    virtual void unset_focus();

private:
    void compose_preedit_array(TxPacket *);
    void compose_feedback_array(TxPacket *);
    void update_CandidateWin();
  
    Connection *mConn;
    int mImid,mIcid;
    int mPrevLen;
};

CandidateWin *g_cur_cand_win;

Convdisp *create_convdisp(int style, KKContext *k,
			  icxatr *a, Connection *c)
{
    switch (style) {
    case IS_ROOT_WINDOW:
	return new ConvdispRw(k, a);
    case IS_OVER_THE_SPOT:
	return new ConvdispOv(k, a);
    case IS_ON_THE_SPOT:
	return new ConvdispOs(k, a, c);
    default:
	break;
    }
    return 0;
}

//
// PeWin(PreeEdit Window)
//
PeWin::PeWin(Window pw)
{
    mParentWin = pw;
    int scr_num = DefaultScreen(gDpy);
    // とりあえずテキトー
    mWidth = 1;
    mHeight = 1;
    mWin = XCreateSimpleWindow(gDpy, mParentWin,
			       0, 0, mWidth, mHeight, 0,
			       BlackPixel(gDpy, scr_num),
			       WhitePixel(gDpy, scr_num));
    XSetWindowAttributes attr;
    attr.override_redirect = True;
    XChangeWindowAttributes(gDpy, mWin, CWOverrideRedirect,
			    &attr);
    mPixmap = XCreatePixmap(gDpy, DefaultRootWindow(gDpy),
			    mWidth, mHeight,
			    DefaultDepth(gDpy, scr_num));
    
    mGC = XCreateGC(gDpy, mPixmap, 0, 0);
    mClearGC = XCreateGC(gDpy, mPixmap, 0, 0);
    mHilitGC = XCreateGC(gDpy, mPixmap, 0, 0);
    
    XSetBackground(gDpy, mGC, WhitePixel(gDpy, scr_num));
    XSetForeground(gDpy, mGC, BlackPixel(gDpy, scr_num));
    XSetForeground(gDpy, mClearGC, WhitePixel(gDpy, scr_num));
    XSetBackground(gDpy, mClearGC, BlackPixel(gDpy, scr_num));

    add_window_watch(mWin, this, EXPOSE_MASK|STRUCTURE_NOTIFY_MASK);
    mIsMapped = false;//マップされてない
    mFontset = gFontset;
    
    XFlush(gDpy);
}

PeWin::~PeWin()
{
    if (mWin) {
        release();
    }

    XFreePixmap(gDpy, mPixmap);
    
    XFreeGC(gDpy, mGC);
    XFreeGC(gDpy, mClearGC);
    XFreeGC(gDpy, mHilitGC);

    XFlush(gDpy);
}

void PeWin::release()
{
    remove_window_watch(mWin);
    XDestroyWindow(gDpy, mWin);
    mWin = 0;
}

void PeWin::destroy(Window w)
{
    if (mWin && mWin == w) {
        mWin = 0;
    }
}

void PeWin::expose(Window w)
{
    XCopyArea(gDpy, mPixmap, mWin, mGC,
              0, 0, mWidth, mHeight, 0, 0);

    XFlush(gDpy);
}

void PeWin::draw_char(int x,int y,cchar ch,int stat)
{
    GC gc = mGC;
    if (stat & PE_REVERSE) {
	gc = mClearGC;
    }
    if (stat & PE_HILIGHT) {
        //gc = mHilitGC;
    }
    
    if (ch < 256) {
	char c = ch;
	XmbDrawImageString(gDpy, mPixmap, mFontset, gc, x, y, &c, 1);
    } else {
	unsigned char c[2];
	c[0] = ((ch >>8)&255)|0x80;
	c[1] = (ch & 255)|0x80;

	XmbDrawImageString(gDpy, mPixmap, mFontset,
			   gc, x, y, (char *)c, 2);
    }
}

void PeWin::set_back(unsigned long p)
{
    mBack = p;
    XSetBackground(gDpy, mGC, p);
    XSetForeground(gDpy, mClearGC, p);
}

void PeWin::set_fore(unsigned long p)
{
    mFore = p;
    XSetForeground(gDpy, mGC, p);
    XSetBackground(gDpy, mClearGC, p);
}

void PeWin::set_fontset(XFontSet f)
{
    mFontset = f;
}

void PeWin::set_size(int w, int h)
{
    if (w == mWidth && h == mHeight) {
        return ;
    }
    XResizeWindow(gDpy, mWin, w, h);
    XFreePixmap(gDpy, mPixmap);
    mPixmap =XCreatePixmap(gDpy, DefaultRootWindow(gDpy), w, h,
                            DefaultDepth(gDpy, DefaultScreen(gDpy)));
    mWidth = w;
    mHeight = h;
    clear();
}

void PeWin::set_pos(int x, int y)
{
    XWindowChanges ch;
    ch.x = x;
    ch.y = y;
    XConfigureWindow(gDpy, mWin, CWX|CWY, &ch);
}

void PeWin::do_map()
{
    if (!mIsMapped) {
        XFlush(gDpy);
        XMapRaised(gDpy, mWin);
        mIsMapped = true;
    }
}

void PeWin::clear()
{
    XFillRectangle(gDpy, mPixmap, mClearGC,
		   0, 0, mWidth, mHeight);
}

void PeWin::draw()
{
    if (!mIsMapped) {
        do_map();
    } else {
        expose(mWin);
    }
}

void PeWin::unmap()
{
    if (mIsMapped) {
        XUnmapWindow(gDpy, mWin);
	XFlush(gDpy);
	mIsMapped = false;
    }
}

//
// PeLineWin
//
PeLineWin::PeLineWin(Window w) : PeWin(w)
{
    set_size(400, 40);
    clear();
}

PeLineWin::~PeLineWin()
{
}

void PeLineWin::draw_pe(pe_stat *p)
{
    clear();
    calc_extent(p);
    m_x = 0;
    std::list<pe_jstring>::iterator i;
    for (i = p->jstrings.begin(); i != p->jstrings.end(); i++) {
        draw_segment(&(*i));
    }
}

void PeLineWin::draw_segment(pe_jstring *s)
{
    jstring_t::iterator i;
    for (i = s->s.begin(); i != s->s.end(); i++) {
        cchar ch= *i;
        draw_char(m_x, 20, ch, s->stat);
        m_x += 20;
    }
}

void PeLineWin::calc_extent(pe_stat *p)
{
    int c;
    c = p->get_char_count();
    set_size(400, 40);
}


//
// PeOvWin
//
PeOvWin::PeOvWin(Window w) : PeWin(w)
{
    m_mask_pix = 0;
    m_mask_pix_gc = 0;
}

PeOvWin::~PeOvWin()
{
    if (m_mask_pix) {
        XFreePixmap(gDpy, m_mask_pix);
        XFreeGC(gDpy, m_mask_pix_gc);
    }
}

void PeOvWin::set_size(int w, int h)
{
    if (w == mWidth && h == mHeight) {
        return ;
    }
    PeWin::set_size(w, h);
    if (m_mask_pix) {
        XFreePixmap(gDpy, m_mask_pix);
        m_mask_pix = XCreatePixmap(gDpy, mWin,
                                   mWidth, mHeight, 1);
    }
}

void PeOvWin::draw_ce(char_ent *ce, int len)
{
    if (m_mask_pix == 0) {
	m_mask_pix = XCreatePixmap(gDpy, mWin, mWidth, mHeight, 1);
	m_mask_pix_gc = XCreateGC(gDpy, m_mask_pix, 0, 0);
    }


    clear();
    XSetForeground(gDpy, m_mask_pix_gc, BlackPixel(gDpy, DefaultScreen(gDpy)));
    XFillRectangle(gDpy, m_mask_pix, m_mask_pix_gc, 0, 0, mWidth, mHeight);
    XSetForeground(gDpy, m_mask_pix_gc, WhitePixel(gDpy, DefaultScreen(gDpy)));
    int i;
    for (i = 0; i < len; i++) {
	draw_a_ce(&ce[i]);
    }
    XShapeCombineMask(gDpy, mWin, ShapeBounding,
		      0, 0, m_mask_pix, ShapeSet);
    do_map();
}

void PeOvWin::draw_a_ce(char_ent *ce)
{
    draw_char(ce->x, ce->y, ce->c, ce->stat);
    XFillRectangle(gDpy, m_mask_pix, m_mask_pix_gc,
                   ce->x, ce->y - ce->height + 2,
                   ce->width, ce->height+1);
    if (ce->stat & PE_UNDERLINE) {
        XDrawLine(gDpy, mPixmap, mGC, ce->x, ce->y+2,
		  ce->x+ce->width, ce->y + 2);
    }
}

//
//
//
CandidateWin::CandidateWin(Convdisp *c)
{
    mConvdisp = c;
    gchar *titles[2]={"index","候補"};
    bInit = false;
    m_pe = 0;
    
    top_win = gtk_window_new(GTK_WINDOW_POPUP);
    gtk_widget_set_usize(top_win, CAND_WIN_WIDTH, CAND_WIN_HEIGHT);
    clist = gtk_clist_new_with_titles(2, titles);
    gtk_container_add(GTK_CONTAINER(top_win), clist);
    gtk_clist_set_column_width(GTK_CLIST(clist), 0, 50);
    gtk_clist_set_column_width(GTK_CLIST(clist), 1, 150);
    
    gtk_widget_show(clist);
    add_widget_watch(clist, WIDGET_ROW_SELECTED, this);
    add_widget_watch(top_win, WIDGET_KEY_PRESS, this);
    m_select_count = 0;
}

CandidateWin::~CandidateWin()
{
    if (g_cur_cand_win == this) {
	g_cur_cand_win = 0;
    }
    gtk_widget_destroy(top_win);
}

void CandidateWin::select_row(GtkWidget *w, int row, int col)
{
    if (m_select_count == 0) {
	m_pe->cands->nth = row;
	mConvdisp->candidate_selected(row);
    }else{
	m_select_count --;
    }
}

void CandidateWin::key_press(GtkWidget *w, GdkEventKey *e)
{
    keyState k(e);
    if (k.is_modifier()) {
        return ;
    }
    m_pe->cont->pushKey(&k);
}

void CandidateWin::set_pos(int x, int y)
{
    if (x == INVALID_POS) {
	x = (gdk_screen_width() - CAND_WIN_WIDTH) / 2;
    }
    if (y == INVALID_POS) {
	y = gdk_screen_height() - CAND_WIN_HEIGHT;
    }
    gtk_widget_set_uposition(top_win, x, y);
}

void CandidateWin::update(pe_stat *pe)
{
    m_pe = pe;
    if (pe->cands) {
	int nth = m_pe->cands->nth;
	if (!bInit) {
	    m_base_row = 0;
	    bInit = true;
	    build_candidate_list();
	    m_select_count = 1;
	}
    
	if (trim_base_row()) {
	    build_candidate_list();
	    m_select_count = 1;
	}

	show();
	gtk_clist_select_row((GtkCList*)clist, nth-m_base_row, 0);
	m_select_count ++;
    } else {
	bInit = false;
	gtk_widget_hide(top_win);
	gtk_clist_clear((GtkCList*)clist);
    }
}

bool CandidateWin::trim_base_row()
{
    int nth = m_pe->cands->nth;

    if (m_base_row +CAND_COUNT > nth && m_base_row <= nth) {
	return false;
    }
    while (m_base_row +CAND_COUNT <= nth) {
	m_base_row += CAND_COUNT;
    }
    while (m_base_row > nth) {
	m_base_row -=CAND_COUNT;
    }
    return true;
}

void CandidateWin::build_candidate_list()
{
    int i;
    int size = m_pe->cands->cands.size();
    char idx[12];
    char *buf[2];
    gtk_clist_clear((GtkCList*)clist);
    for (i = m_base_row; i < (int)size && i < m_base_row + CAND_COUNT; i++) {
	char *c;
	if (i == m_base_row && i > 0) {
	    sprintf(idx,"%d(<<)",i+1);
	} else if (i == m_base_row + CAND_COUNT - 1 && i < size) {
	    sprintf(idx,"%d(>>)", i+1);
	} else {
	    sprintf(idx,"%d", i+1);
	}
	c = jstring_to_str(&m_pe->cands->cands[i]);
	buf[0]= idx;
	buf[1] = c;
	gtk_clist_append((GtkCList*)clist, buf);
	free(c);
    }
    sprintf(idx, "(%d/%d)", m_base_row, size);
    buf[0] = "候補数";
    buf[1] = idx;
    gtk_clist_append((GtkCList*)clist, buf);
}

void CandidateWin::show()
{
    if (m_pe && m_pe->cands && bInit) {
	if (g_cur_cand_win && g_cur_cand_win !=this) {
	    g_cur_cand_win->hide();
	}
	g_cur_cand_win = this;
	gtk_widget_show(top_win);
    }
}

void CandidateWin::hide()
{
    gtk_widget_hide(top_win);
}

//
//
//
Convdisp::Convdisp(KKContext *k, icxatr *a)
{
    mKkContext = k;
    m_atr = a;
    m_cands_win = new CandidateWin(this);
}

Convdisp::~Convdisp()
{
    delete m_cands_win;
}

void Convdisp::set_pe(pe_stat *p)
{
    m_pe = p;
}

void Convdisp::candidate_selected(int n)
{
    if (n == 12) {
        return ;
    }
    mKkContext->candidate_selected(n);
}

//ルートウィンドウスタイル
ConvdispRw::ConvdispRw(KKContext *k, icxatr *a) : Convdisp(k, a)
{
    mPeWin = new PeLineWin(DefaultRootWindow(gDpy));
}

ConvdispRw::~ConvdispRw()
{
    delete mPeWin;
}

void ConvdispRw::update_preedit()
{
    if (m_pe && m_pe->get_char_count()) {
	// preeditの中身が存在すれば
	mPeWin->do_map();
	mPeWin->draw_pe(m_pe);
	mPeWin->draw();
    } else {
	mPeWin->clear();
	mPeWin->expose(0);
	XFlush(gDpy);
    }
    m_cands_win->set_pos(0, 0);
    m_cands_win->update(m_pe);
}

void ConvdispRw::update_icxatr()
{
}

//Over the spot style
ConvdispOv::ConvdispOv(KKContext *k, icxatr *a) : Convdisp(k, a)
{
    m_ov_win = 0;
}

ConvdispOv::~ConvdispOv()
{
    if (m_ov_win) {
	delete m_ov_win;
    }
}

void ConvdispOv::update_preedit()
{
    if (!m_pe) {
	return ;
    }
    if (!m_pe->get_char_count()) {
	delete m_ov_win;
	m_ov_win = NULL;
	m_cands_win->update(m_pe);
	return ;
    }

    // preeditを書く
    drawPreedit();

    // update candidate window
    if (m_atr->has_atr(ICA_FocusWindow)
	&& m_atr->has_atr(ICA_SpotLocation)){
	int x, y;
	Window win;
	XTranslateCoordinates(gDpy,m_atr->focus_window,
			      DefaultRootWindow(gDpy),
			      m_atr->spot_location.x,
			      m_atr->spot_location.y,
			      &x, &y, &win);
	if (x + CAND_WIN_WIDTH > scr_width) {
	    x = scr_width - CAND_WIN_WIDTH;
	}
	if (x < 0) {
	    x = 0;
	}
	if (y + CAND_WIN_HEIGHT > scr_height) {
	    //上側に候補ウィンドウを出す。
	    y -= (CAND_WIN_HEIGHT+m_atr->line_space+2);
	}else{
	    // 下側に候補ウィンドウを出す。
	    y += 2;
	}
	m_cands_win->set_pos(x, y);
    }else{
	m_cands_win->set_pos(INVALID_POS, INVALID_POS);
    }
    m_cands_win->update(m_pe);
}

void ConvdispOv::validate_area()
{
    Window r, win;
    int x;
    unsigned int w, h, tmp;
    if (m_atr->has_atr(ICA_ClientWindow)) {
	win = m_atr->client_window;
    } else {
	win = m_atr->focus_window;
    }
    XGetGeometry(gDpy, win,
		 &r, &x, &x, &w, &h, &tmp, &tmp);
    /* そんな無茶な,, (RedHat7.3のQt対策、たぶん他のQtもこの動作)*/
    m_atr->area.width = w;
    m_atr->area.height = h;
}

void ConvdispOv::update_icxatr()
{
    if (!m_ov_win) {
        return ;
    }
    
    if (m_atr->is_changed(ICA_FocusWindow)) {
	//なんか忘れたけど、FocusWindowを後から指定してくるものがある
        if (!check_win()) {
            return ;
        }
    }
    
    if (m_atr->is_changed(ICA_Area)) {
	if (m_atr->area.width == 0) {
	    validate_area();
	}
	m_ov_win->set_size(m_atr->area.width, m_atr->area.height);
	m_atr->unset_change_mask(ICA_Area);
    }
  
    if (m_atr->is_changed(ICA_Foreground)) {
	m_ov_win->set_fore(m_atr->foreground_pixel);
	m_atr->unset_change_mask(ICA_Foreground);
    }
    if (m_atr->is_changed(ICA_Background)) {
	m_ov_win->set_back(m_atr->background_pixel);
	m_atr->unset_change_mask(ICA_Background);
    }
    if (m_atr->is_changed(ICA_FontSet)) {
	m_ov_win->set_fontset(m_atr->font_set);
	m_atr->unset_change_mask(ICA_FontSet);
    }
  
    update_preedit();
}

void ConvdispOv::set_focus()
{
    m_cands_win->show();
}

void ConvdispOv::unset_focus()
{
}

void ConvdispOv::drawPreedit()
{
    m_ce_len = m_pe->get_char_count();
    if (!check_win()) {
	return ;
    }

    m_ce = (char_ent *)malloc(sizeof(char_ent)*m_ce_len);
    make_ce_array();//配列を準備して、
    layoutCharEnt();//レイアウトして、
    do_draw_preedit();//描画
    free(m_ce);
    m_cands_win->update(m_pe);
    m_ov_win->draw();
    XFlush(gDpy);
}

void ConvdispOv::do_draw_preedit()
{
    m_ov_win->set_pos(0, 0);
    m_ov_win->draw_ce(m_ce, m_ce_len);
}

bool ConvdispOv::check_win()
{
    if (!check_atr()) {
	//プリエディットウィンドウを出すのに十分な情報が無い。
	return false;
    }
  
    if (m_ov_win && !m_atr->is_changed(ICA_FocusWindow)) {
	// ウィンドウをupdateする必要は無い。
	return true;
    }

    if (m_ov_win) {
	delete m_ov_win;
    }
  
    m_atr->unset_change_mask(ICA_FocusWindow);
    m_atr->unset_change_mask(ICA_Foreground);
    m_atr->unset_change_mask(ICA_Background);
    m_atr->unset_change_mask(ICA_FontSet);

    Window w;
    if (m_atr->has_atr(ICA_ClientWindow)) {
	w = m_atr->client_window;
    } else {
	w = m_atr->focus_window;
    }

    m_ov_win = new PeOvWin(w);
    m_ov_win->set_size(m_atr->area.width, m_atr->area.height);
    m_ov_win->set_fore(m_atr->foreground_pixel);
    m_ov_win->set_back(m_atr->background_pixel);
    m_ov_win->set_fontset(m_atr->font_set);
  
    return true;
}

bool ConvdispOv::check_atr()
{
    if (!m_atr->has_atr(ICA_FocusWindow) &&
	!m_atr->has_atr(ICA_ClientWindow)) {
	return false;
    }
    if (!m_atr->has_atr(ICA_SpotLocation)) {
	//入力場所が未定なら左上
	m_atr->spot_location.x = 0;
	m_atr->spot_location.y = 0;
    }
    if (!m_atr->has_atr(ICA_Area) ||
	m_atr->area.width == 0) {
	validate_area();
	m_atr->area.x = 0;
	m_atr->area.y = 0;
    }
    if (!m_atr->has_atr(ICA_FontSet)) {
	m_atr->font_set = gFontset;
    }
    if (!m_atr->has_atr(ICA_LineSpace)) {
	m_atr->line_space = 14;
    }
    if (!m_atr->has_atr(ICA_Foreground)) {
	m_atr->foreground_pixel = BlackPixel(gDpy, DefaultScreen(gDpy));
    }
    if (!m_atr->has_atr(ICA_Background)) {
	m_atr->background_pixel = WhitePixel(gDpy, DefaultScreen(gDpy));
    }
    return true;
}

void ConvdispOv::make_ce_array()
{
    std::list<pe_jstring>::iterator i;
    jstring_t::iterator j;
    int s;
    int c = 0;
    for (i = m_pe->jstrings.begin(); i != m_pe->jstrings.end(); i++) {
	s = (*i).stat;
	for (j = (*i).s.begin(); j != (*i).s.end(); j++) {
	    m_ce[c].c = *j;
	    m_ce[c].stat = s;
	    c++;
	}
    }
}

void ConvdispOv::layoutCharEnt()
{
    int i;
    int x,y;

    x = m_atr->spot_location.x;
    y = m_atr->spot_location.y;

    for (i = 0; i < m_ce_len; i++) {
	cchar ch = m_ce[i].c;
	char buf[2];
	int l;
	XRectangle ink,logical;
	if (ch < 256) {
	    buf[0] = ch;
	    l = 1;
	} else {
	    buf[1] = (ch & 255)|0x80;
	    buf[0] = ((ch>>8)&255)|0x80;
	    l = 2;
	}
	XmbTextExtents(m_atr->font_set, buf, l, &ink, &logical);
	m_ce[i].width = logical.width;
	m_ce[i].height = logical.height;
	if (m_ce[i].width + x >
	    m_atr->area.width /*+ m_atr->area.x*/) {
	    x = 0;/*m_atr->area.x;*/
	    y += m_atr->line_space;
	}
	m_ce[i].x = x;
	m_ce[i].y = y;
	x += m_ce[i].width;
    }
}

int ConvdispOv::calc_ce_width(int b, int e)
{
    int i,w=0;
    for (i = b; i < e; i++) {
        w += m_ce[i].width;
    }
    return w;
}

ConvdispOs::ConvdispOs(KKContext *k, icxatr *a, Connection *c)
    : Convdisp(k, a)
{
    XimIC *ic=k->get_ic();
    mConn = c;
    mImid = ic->get_imid();
    mIcid = ic->get_icid();
    mPrevLen = 0;
}

ConvdispOs::~ConvdispOs()
{
}

void ConvdispOs::update_preedit()
{
    if (!m_pe) {
        return ;
    }
    update_CandidateWin();
    TxPacket *t;

    int len;
    len = m_pe->get_char_count();

    if (mPrevLen == 0 && len == 0) {
	return ;
    }
    if (mPrevLen == 0 && len) {
	t = createTxPacket(XIM_PREEDIT_START, 0);
	t->pushC16(mImid);
	t->pushC16(mIcid);
	mConn->push_passive_packet(t);
    }

    t = createTxPacket(XIM_PREEDIT_DRAW, 0);
    t->pushC16(mImid);
    t->pushC16(mIcid);
    t->pushC32(0);// caret
    t->pushC32(0); // chg_first
    t->pushC32(mPrevLen); // chg_length
    //feedback array をセットしないとmozillaが落ちるが、それはIMEが悪いと考えるのが普通だろう。
    if (m_pe->jstrings.size()) {
	t->pushC32(0);
    } else {
	t->pushC32(3);
    }
  
    compose_preedit_array(t);
    compose_feedback_array(t);
    mConn->push_passive_packet(t);

    if (mPrevLen && len == 0) {
	t = createTxPacket(XIM_PREEDIT_DONE, 0);
	t->pushC16(mImid);
	t->pushC16(mIcid);
	mConn->push_passive_packet(t);
    }
    mPrevLen = len;
    /*
      t = createTxPacket(XIM_PREEDIT_CARET,0);
      t->pushC16(mImid);
      t->pushC16(mIcid);
      t->pushC32(pos);
      t->pushC32(dir);
      t->pushC32(style);
      mConn->push_passive_packet(t);
      */
      
}

void ConvdispOs::update_icxatr()
{
}

void ConvdispOs::compose_preedit_array(TxPacket *t)
{
    jstring_t s;
    std::list<pe_jstring>::iterator it;
    for (it = m_pe->jstrings.begin(); it != m_pe->jstrings.end(); it++) {
        append_jstring(&s, &(*it).s);
    }

    char *c = jstring_to_ctext(&s);
    int i,len;
    len = strlen(c);
    t->pushC16(len);// LENGTH
    for (i = 0 ; i < len ; i++) {
        t->pushC8(c[i]); // CTEXT
    }
    len = pad4(len+2);
    for (i = 0; i < len; i++) {
        t->pushC8(0); // PADDING
    }
    free(c);
}

void ConvdispOs::compose_feedback_array(TxPacket *t)
{
    int i,len,stat,xstat;
    len = m_pe->get_char_count();
    t->pushC16(len*4);
    t->pushC16(0);
    std::list<pe_jstring>::iterator it;
    for (it = m_pe->jstrings.begin(); it != m_pe->jstrings.end(); it++) {
        len = (*it).s.size();
        stat = (*it).stat;
        xstat = FB_None;
        if (stat & PE_REVERSE) {
            xstat |= FB_Reverse;
        }
        if (stat & PE_UNDERLINE) {
            xstat |= FB_Underline;
        }
        if (stat & PE_HILIGHT) {
            xstat |= FB_Highlight;
        }
        for (i = 0; i < len; i++) {
            t->pushC32(xstat);
        }
    }
}

void ConvdispOs::set_focus()
{
    m_cands_win->show();
}

void ConvdispOs::unset_focus()
{
}

void ConvdispOs::update_CandidateWin()
{
    m_cands_win->set_pos(INVALID_POS, INVALID_POS);
    m_cands_win->update(m_pe);
}
/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */
