/*

  Copyright (c) 2003,2004 uim Project http://uim.freedesktop.org/

  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
  3. Neither the name of authors nor the names of its contributors
     may be used to endorse or promote products derived from this software
     without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  SUCH DAMAGE.
*/

// classes for preedit draw

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

//
// PeWin: Base class for preedit window
//  PeLineWin  root window style
//  PeOvWin  over the spot

static XFontSet gFontset;

void init_convdisp()
{
    char **missing, *def;
    int nr_missing;
    gFontset = 
	XCreateFontSet(XimServer::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);
    // same fontset as kterm

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

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

// Preedit 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, uchar 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;
};

// one line preedit window for RootWindowStyle
class PeLineWin : public PeWin {
public:
    PeLineWin(Window w);
    virtual ~PeLineWin();

    void draw_pe(pe_stat *p);

    void draw_segment(pe_ustring *s);

private:
    void calc_extent(pe_stat *p);

    int m_x;
};

// window for over the spot style
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;
};

class ConvdispOv : public Convdisp{
public:
    ConvdispOv(InputContext *,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 draw_preedit();
    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;
};

// Preedit window for RootWindowStyle
class ConvdispRw : public Convdisp{
public:
    ConvdispRw(InputContext *,icxatr *);
    virtual ~ConvdispRw();

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

class Connection;
class ConvdispOs : public Convdisp{
public:
    ConvdispOs(InputContext *,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 *);
  
    Connection *mConn;
    int mImid,mIcid;
    int mPrevLen;
};

Convdisp *create_convdisp(int style, InputContext *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(PreEdit Window)
//
PeWin::PeWin(Window pw)
{
    mParentWin = pw;
    int scr_num = DefaultScreen(XimServer::gDpy);
    // tentative
    mWidth = 1;
    mHeight = 1;
    mWin = XCreateSimpleWindow(XimServer::gDpy, mParentWin,
			       0, 0, mWidth, mHeight, 0,
			       BlackPixel(XimServer::gDpy, scr_num),
			       WhitePixel(XimServer::gDpy, scr_num));
    XSetWindowAttributes attr;
    attr.override_redirect = True;
    XChangeWindowAttributes(XimServer::gDpy, mWin, CWOverrideRedirect,
			    &attr);
    mPixmap = XCreatePixmap(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy),
			    mWidth, mHeight,
			    DefaultDepth(XimServer::gDpy, scr_num));
    
    mGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
    mClearGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
    mHilitGC = XCreateGC(XimServer::gDpy, mPixmap, 0, 0);
    
    XSetBackground(XimServer::gDpy, mGC, WhitePixel(XimServer::gDpy, scr_num));
    XSetForeground(XimServer::gDpy, mGC, BlackPixel(XimServer::gDpy, scr_num));
    XSetForeground(XimServer::gDpy, mClearGC, WhitePixel(XimServer::gDpy, scr_num));
    XSetBackground(XimServer::gDpy, mClearGC, BlackPixel(XimServer::gDpy, scr_num));

    add_window_watch(mWin, this, EXPOSE_MASK|STRUCTURE_NOTIFY_MASK);
    mIsMapped = false;//not mapped now
    mFontset = gFontset;
    
    XFlush(XimServer::gDpy);
}

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

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

    XFlush(XimServer::gDpy);
}

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

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

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

    XFlush(XimServer::gDpy);
}

void PeWin::draw_char(int x, int y, uchar 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(XimServer::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(XimServer::gDpy, mPixmap, mFontset,
			   gc, x, y, (char *)c, 2);
    }
}

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

void PeWin::set_fore(unsigned long p)
{
    mFore = p;
    XSetForeground(XimServer::gDpy, mGC, p);
    XSetBackground(XimServer::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(XimServer::gDpy, mWin, w, h);
    XFreePixmap(XimServer::gDpy, mPixmap);
    mPixmap =XCreatePixmap(XimServer::gDpy, DefaultRootWindow(XimServer::gDpy), w, h,
                            DefaultDepth(XimServer::gDpy, DefaultScreen(XimServer::gDpy)));
    mWidth = w;
    mHeight = h;
    clear();
}

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

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

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

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

void PeWin::unmap()
{
    if (mIsMapped) {
        XUnmapWindow(XimServer::gDpy, mWin);
	XFlush(XimServer::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_ustring>::iterator i;
    for (i = p->ustrings.begin(); i != p->ustrings.end(); i++) {
        draw_segment(&(*i));
    }
}

void PeLineWin::draw_segment(pe_ustring *s)
{
    uString::iterator i;
    for (i = s->s.begin(); i != s->s.end(); i++) {
        uchar 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(XimServer::gDpy, m_mask_pix);
        XFreeGC(XimServer::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(XimServer::gDpy, m_mask_pix);
        m_mask_pix = XCreatePixmap(XimServer::gDpy, mWin,
                                   mWidth, mHeight, 1);
    }
}

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


    clear();
    XSetForeground(XimServer::gDpy, m_mask_pix_gc,
		   BlackPixel(XimServer::gDpy,
			      DefaultScreen(XimServer::gDpy)));
    XFillRectangle(XimServer::gDpy, m_mask_pix,
		   m_mask_pix_gc, 0, 0, mWidth, mHeight);
    XSetForeground(XimServer::gDpy, m_mask_pix_gc,
		   WhitePixel(XimServer::gDpy,
			      DefaultScreen(XimServer::gDpy)));
    int i;
    for (i = 0; i < len; i++) {
	draw_a_ce(&ce[i]);
    }
    XShapeCombineMask(XimServer::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(XimServer::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(XimServer::gDpy, mPixmap, mGC, ce->x, ce->y+2,
		  ce->x+ce->width, ce->y + 2);
    }
}

//
//
//
Convdisp::Convdisp(InputContext *k, icxatr *a)
{
    mKkContext = k;
    m_atr = a;
}

Convdisp::~Convdisp()
{
}

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

ConvdispRw::ConvdispRw(InputContext *k, icxatr *a) : Convdisp(k, a)
{
    mPeWin = new PeLineWin(DefaultRootWindow(XimServer::gDpy));
}

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

void ConvdispRw::update_preedit()
{
    if (m_pe && m_pe->get_char_count()) {
	// preedit string exists
	mPeWin->do_map();
	mPeWin->draw_pe(m_pe);
	mPeWin->draw();
    } else {
	mPeWin->clear();
	mPeWin->expose(0);
	XFlush(XimServer::gDpy);
    }
}

void ConvdispRw::update_icxatr()
{
}

//Over the spot style
ConvdispOv::ConvdispOv(InputContext *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;
	return ;
    }

    draw_preedit();

    if (m_atr->has_atr(ICA_SpotLocation)){
       
	int x = -1, y = -1;
	Window win;

	if(m_atr->has_atr(ICA_FocusWindow)){

	    XTranslateCoordinates(XimServer::gDpy,m_atr->focus_window,
				  DefaultRootWindow(XimServer::gDpy),
				  m_atr->spot_location.x,
				  m_atr->spot_location.y,
				  &x, &y, &win);
	}
      
	if(m_atr->has_atr(ICA_ClientWindow)){

	    XTranslateCoordinates(XimServer::gDpy,m_atr->client_window,
				  DefaultRootWindow(XimServer::gDpy),
				  m_atr->spot_location.x,
				  m_atr->spot_location.y,
				  &x, &y, &win);
	}
	if(x > -1 && y > -1) {
	    Canddisp* disp = canddisp_singleton();
	    disp->move(x, y + 2);
	}
    }
}

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(XimServer::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)) {
	//Some clients send FocusWindow later
        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()
{
    Canddisp* disp = canddisp_singleton();
    disp->show();
}

void ConvdispOv::unset_focus()
{
    Canddisp* disp = canddisp_singleton();
    disp->hide();
}

void ConvdispOv::draw_preedit()
{
    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();//prepare array
    layoutCharEnt();//layout them
    do_draw_preedit();//do draw
    free(m_ce);
    m_ov_win->draw();
    XFlush(XimServer::gDpy);
}

void ConvdispOv::do_draw_preedit()
{
    if (m_atr->has_atr(ICA_Area)) {
	m_ov_win->set_pos(m_atr->area.x, m_atr->area.y);
    } else {
	m_ov_win->set_pos(0, 0);
    }
    m_ov_win->draw_ce(m_ce, m_ce_len);
}

bool ConvdispOv::check_win()
{
    if (!check_atr()) {
	//not enough information to map preedit
	return false;
    }
  
    if (m_ov_win && !m_atr->is_changed(ICA_FocusWindow)) {
	// no need to update window
	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(XimServer::gDpy,
					     DefaultScreen(XimServer::gDpy));
    }
    if (!m_atr->has_atr(ICA_Background)) {
	m_atr->background_pixel = WhitePixel(XimServer::gDpy,
					     DefaultScreen(XimServer::gDpy));
    }
    return true;
}

void ConvdispOv::make_ce_array()
{
    std::list<pe_ustring>::iterator i;
    uString::iterator j;
    int s;
    int c = 0;
    for (i = m_pe->ustrings.begin(); i != m_pe->ustrings.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++;
	}
    }
}

// Dirty,,, 
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++) {
	uchar ch = m_ce[i].c;
	char buf[3];
	int l;
	XRectangle ink,logical;
	if (ch < 256) {
	    buf[0] = ch;
	    buf[1] = 0;
	    l = 1;
	} else {
	    buf[2] = 0;
	    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;
	int right_limit = m_atr->area.width;
	if (m_atr->has_atr(ICA_Area)) {
	    right_limit += m_atr->area.x;
	}
	if (m_ce[i].width + x >
	    right_limit) {
	    // goto next line
	    x = m_atr->area.x;
	    y += m_atr->line_space;
	}
	m_ce[i].x = x;
	m_ce[i].y = y;
	x += m_ce[i].width;
    }
    if (m_atr->has_atr(ICA_Area)) {
	for (i = 0; i < m_ce_len; i++) {
	    m_ce[i].x -= m_atr->area.x;
	    m_ce[i].y -= m_atr->area.y;
	}
    }
}

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(InputContext *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 ;
    }
    TxPacket *t;

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

    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(caret_pos);// caret
    t->pushC32(0); // chg_first
    t->pushC32(mPrevLen); // chg_length

    if (m_pe->ustrings.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);
      */
      
    if (m_atr->has_atr(ICA_FocusWindow)
        && m_atr->has_atr(ICA_SpotLocation)){
	int x, y;
	Window win;
	XTranslateCoordinates(XimServer::gDpy,m_atr->focus_window,
			      DefaultRootWindow(XimServer::gDpy),
			      m_atr->spot_location.x,
			      m_atr->spot_location.y,
			      &x, &y, &win);
        
        Canddisp* disp = canddisp_singleton();
        disp->move(x, y + 2);
    }    
    
}

void ConvdispOs::update_icxatr()
{
}

void ConvdispOs::compose_preedit_array(TxPacket *t)
{
    uString s;
    std::list<pe_ustring>::iterator it;
    for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.end(); it++) {
        append_ustring(&s, &(*it).s);
    }

    char *c = mKkContext->getServer()->uStringToCtext(&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_ustring>::iterator it;
    for (it = m_pe->ustrings.begin(); it != m_pe->ustrings.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()
{
    Canddisp* disp = canddisp_singleton();
    disp->show();
}

void ConvdispOs::unset_focus()
{
    Canddisp* disp = canddisp_singleton();
    disp->hide();
}
/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 * End:
 */
