/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/
/******************************************************************

              Copyright 1992 by Fuji Xerox Co., Ltd.

Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation, and that the name of Fuji Xerox
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.
Fuji Xerox make no representations about the suitability of this
software for any purpose.  It is provided
"as is" without express or implied warranty.

FUJI XEROX DISCLAIM ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL FUJI XEROX BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

  Author: Kazunori Nishihara	Fuji Xerox

******************************************************************/


#include "switchIM.h"
#include "xiiimp.h"
#include "iiimpIM.h"
#include "guiIM.h"
#include "guiIMSts.h"
#include "composeIM.h"
#include "XimpIm.h"
#include <X11/keysym.h>
#include <wchar.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include "status.h"
#include "lookup.h"
#include "codeinput.h"
#include "tableinput.h"
#include "trace_message.h"

static Bool _Ximp_Local_OpenIM_hook(Ximp_XIM);
static Status _Ximp_Local_CloseIM(XIM);
static char *_Ximp_Local_SetIMValues(XIM, XIMArg*);
static char *_Ximp_Local_GetIMValues(XIM, XIMArg*);
static XIC _Ximp_Local_CreateIC(XIM, XIMArg*);
extern int _Ximp_ctstombs(XIM, char*, int, char*, int, Status*);
extern int _Ximp_ctstowcs(XIM, char*, int, wchar_t*, int, Status*);
extern int Ximp_ParseStringFile(FILE *, Ximp_XIM);

static
XIMMethods
get_local_im_methods()
{
  static XIMMethodsRec imm;
  if (!imm.close) {
    imm.close = _Ximp_Local_CloseIM;
    imm.set_values = _Ximp_Local_SetIMValues;
    imm.get_values = _Ximp_Local_GetIMValues;
    imm.create_ic = _Ximp_Local_CreateIC;
    imm.ctstombs = _Ximp_ctstombs;
    imm.ctstowcs = _Ximp_ctstowcs;
  }
  return &imm;
}

static Status SWITCH_CloseIM(XIM);

static
XIMMethods
get_switch_im_methods()
{
  static XIMMethodsRec imm;
  if (!imm.close) {
    imm.close = SWITCH_CloseIM;
  }
  return &imm;
}

static void _Ximp_Local_DestroyIC(XIC);
static void _Ximp_Local_SetFocus(XIC);
static void _Ximp_Local_UnSetFocus(XIC);
static char *_Ximp_Local_MbReset(XIC);
static wchar_t *_Ximp_Local_WcReset(XIC);
static char *_Ximp_Local_SetICValues(XIC, XIMArg*);
static char *_Ximp_Local_GetICValues(XIC, XIMArg*);
static int _Ximp_Local_MbLookupString(XIC, XKeyEvent*,
				      char*, int, KeySym*, Status*);
static int _Ximp_Local_WcLookupString(XIC, XKeyEvent*, wchar_t*, int,
				      KeySym*, Status*);

XICMethods
get_local_ic_methods()
{
  static XICMethodsRec icm;
  if (!icm.destroy) {
    icm.destroy = _Ximp_Local_DestroyIC;
    icm.set_focus = _Ximp_Local_SetFocus;
    icm.unset_focus = _Ximp_Local_UnSetFocus;
    icm.set_values = _Ximp_Local_SetICValues;
    icm.get_values = _Ximp_Local_GetICValues;
    icm.mb_reset = _Ximp_Local_MbReset;
    icm.wc_reset = _Ximp_Local_WcReset;
    icm.mb_lookup_string = _Ximp_Local_MbLookupString;
    icm.wc_lookup_string = _Ximp_Local_WcLookupString;
  }
  return &icm;
}

static void SWITCH_DestroyIC(XIC);

static
XICMethods
get_switch_ic_methods()
{
  static XICMethodsRec icm;
  if (!icm.destroy) {
    icm.destroy = SWITCH_DestroyIC;
    icm.set_focus = _Ximp_Local_SetFocus;
    icm.unset_focus = _Ximp_Local_UnSetFocus;
  }
  return &icm;
}

static char *_Ximp_Local_SetICValueData(Ximp_XIC, XIMArg *,
					int, XimpChangeaMask);

extern Bool	SwitchRemoteIMState(XicCommon ic, char *localename);

#define LOCAL_MB_BUFSIZE 128

static Bool
_LocalConnect(XimCommon im) {
  int num_local_im_styles;

  im->ximp_impart->type_list      = NULL; /* dummy */
  im->ximp_impart->im_server_name = NULL; /* dummy */
  im->ximp_impart->im_vendor_name = NULL; /* dummy */
  im->ximp_impart->im_proto_vl    = NULL; /* dummy */
  im->ximp_impart->im_proto_vnum  = 0; /* dummy */
  im->ximp_impart->im_ext_list    = 0; /* dummy */

  if (_Ximp_Local_OpenIM_hook((Ximp_XIM)im) == False) return (False);

  im->ximp_impart->im_styles = (XIMStyles *)Xmalloc(sizeof(XIMStyles));
  if (im->ximp_impart->im_styles) {
    char *state_name = (char *)NULL;
    if (im->local_impart->top_state) {
      if (im->local_impart->use_binary_table)
	state_name = BCF_STR_STRING(im->local_impart->str_addr,
				    BCF_STATE_NAME(im->local_impart->state_addr,
						   (CARD32)(im->local_impart->top_state)));
      else
	state_name = im->local_impart->top_state->name;
    }
    if ((state_name != (char *)NULL) && (state_name[0] != '\0')) {
      /* 'name' other than "" will be displayed in the status
	 area, so XIMStatusArea input style should be supported. */
      num_local_im_styles = 6;
    } else {
      num_local_im_styles = 3;
    }
    im->ximp_impart->im_styles->supported_styles = 
      (XIMStyle *)Xmalloc(sizeof(XIMStyle) * num_local_im_styles);
    im->ximp_impart->im_styles->count_styles = num_local_im_styles;
    if (im->ximp_impart->im_styles->supported_styles == NULL ) {
      Xfree(im->ximp_impart->im_styles );
      im->ximp_impart->im_styles = NULL;
      return( False );
    }
    /* XIMPreeditPosition is a fake for Motif */
    im->ximp_impart->im_styles->supported_styles[0] =
      XIMPreeditNone | XIMStatusNone;
    im->ximp_impart->im_styles->supported_styles[1] =
      XIMPreeditNothing | XIMStatusNothing;
    if ((state_name != (char *)NULL) && (state_name[0] != '\0')) {
      im->ximp_impart->im_styles->supported_styles[2] =
	XIMPreeditPosition | XIMStatusArea;
      im->ximp_impart->im_styles->supported_styles[3] =
	XIMPreeditNone | XIMStatusArea;
      im->ximp_impart->im_styles->supported_styles[4] =
	XIMPreeditNothing | XIMStatusArea;
      im->ximp_impart->im_styles->supported_styles[5] =
	XIMPreeditNone | XIMStatusNothing;
    } else {
      im->ximp_impart->im_styles->supported_styles[2] =
	XIMPreeditNone | XIMStatusNothing;
    }
  }
  else {
    return(False);
  }

  return(True);
}

static int
ReadBinaryTable(XIMComposeIM local_impart, FILE *fp)
{
    
    int		fd;
    struct stat	f_stat;
    void	*map_ptr;

    /* mmap */
    fd = fileno(fp);
    if(fstat(fd, &f_stat))
	return -1;
    map_ptr = mmap(0, f_stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if(map_ptr == MAP_FAILED)
	return -1;

    /* set up XIMlocalIMRec struct */
    local_impart->map_addr = map_ptr;
    local_impart->map_len = f_stat.st_size;
    local_impart->state_addr = BCF_STATE_PTR(map_ptr);
    local_impart->def_addr = BCF_DEF_PTR(map_ptr);
    local_impart->str_addr = BCF_STR_PTR(map_ptr);
    local_impart->top_state = (LocalIMState *)1;

    return 0;
}

static Bool
CreateDefTree(im)
XimCommon im;
{
    FILE *fp;
    char *name;
    Bool return_value = True;
    CARD16	signature;

    name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
    if (name == (char *)NULL)
	return(False);
    fp = fopen(name, "r");
    Xfree(name);
    if (fp == (FILE *)NULL){
	return(False);
    }

    im->local_impart->top_state = (LocalIMState *)NULL;

    /* read first 2 bytes */
    if(fread(&signature, sizeof(CARD16), 1, fp) != 1){
	fclose(fp);
	return False;
    }

    /* binary compose file */
    if(signature == BCF_HDR_SIGNATURE){
	im->local_impart->use_binary_table = True;
	im->local_impart->map_addr = (void *)NULL;
	if(ReadBinaryTable(im->local_impart, fp) < 0)
	    return_value =  False;
    }
    /* text compose file */
    else{
	im->local_impart->use_binary_table = False;
	fseek(fp, 0L, SEEK_SET);
#ifdef BACKTRACK_WHEN_UNMATCHED
	im->ximp_impart->num_save_rooms = 
	    Ximp_ParseStringFile(fp, im);
#else
	if(Ximp_ParseStringFile(fp, (Ximp_XIM)im) < 0)
	    return_value = False;
#endif
    }

    fclose(fp);
    return (return_value);
}

static Bool
_Ximp_Local_OpenIM_hook(xim)
Ximp_XIM xim;
{
  XimCommon im = (XimCommon)xim;
  im->local_impart->current_ic = (XIC)NULL;
  return (CreateDefTree(im));
}

XIM
_XimpLocalOpenIM(XLCd lcd, Display *dpy, XrmDatabase rdb,
		 char *res_name, char*res_class) {
  XimCommon im = 0;
  int i;
  char *mod, im_name[BUFSIZE];

  im = (XimCommon)Xmalloc(sizeof(XimCommonRec));
  if (!im) return((XIM)NULL);
  memset(im, 0, sizeof(XimCommonRec));

  if (!CommonOpenIM((XIM)im, lcd, dpy, rdb, res_name, res_class))
    goto Set_Error;

  im->methods = get_local_im_methods();

  im_name[0] = '\0';
  i = 0;
  if ((lcd->core->modifiers) && (*lcd->core->modifiers)) {
#define	MODIFIER "@im="
    mod = strstr(lcd->core->modifiers, MODIFIER);
    if (mod) {
      mod += strlen(MODIFIER);
      while (*mod && *mod != '@' && i < BUFSIZE - 1) {
	im_name[i++] = *mod++;
      }
      im_name[i] = '\0';
    }
  }
#undef MODIFIER

  if (!((0 == strcmp(im_name, "")) ||
	(0 == strcmp(im_name, "none")) ||
	(0 == strcmp(im_name, "local")))) {
    goto Set_Error;
  }

  if (!COMPOSE_OpenIM_SWITCH((XIM)im, lcd)) goto Set_Error;
  return (XIM)im;
 Set_Error:
  if (im) Xfree(im);
  return 0;
}

Status
COMPOSE_OpenIM_SWITCH(XIM xim, XLCd lcd) {
  XimCommon im = (XimCommon)xim;
  XIMComposeIM local_impart = 0;
  char * name;

  local_impart = (XIMComposeIM)Xmalloc(sizeof(XIMComposeIMRec));
  if (!local_impart) goto Set_Error;

  memset(local_impart, 0, sizeof(XIMComposeIMRec));

  im->local_impart = local_impart;

  XIM_COMPOSE(im, switch_methods) = get_switch_im_methods();

  name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
  if (name == (char *)NULL || access(name, R_OK) == -1) {
    Xfree(name);
    goto Set_Error;
  }
  Xfree(name);

  if (!_LocalConnect(im)) goto Set_Error;

  return True;

Set_Error:
  _Ximp_Local_CloseIM((XIM)im);
  return False;
}

static void
FreeDefTreeElements(DefTree *top) {
  if (top->succession) FreeDefTreeElements(top->succession);
  if (top->next) FreeDefTreeElements(top->next);
  if (top->mb) Xfree(top->mb);
  if (top->target_name) Xfree(top->target_name);
  Xfree(top);
}

static Status
_Ximp_Local_CloseIM(xim)
XIM xim;
{
  CommonCloseIM(xim);
  SWITCH_CloseIM(xim);
  return (True);
}

static char *
_Ximp_Local_SetIMValues(xim, arg)
XIM xim;
XIMArg *arg;
{
  return(arg->name);		/* evil */
}

static char *
_Ximp_Local_GetIMValues(xim, values)
XIM xim;
XIMArg *values;
{
  XimCommon im = (XimCommon)xim;
  XIMArg *p;
  XIMStyles **value;
  XIMStyles *styles;
  int i;
  XIMStyles *p_style = im->ximp_impart->im_styles;

  if (im->local_impart == NULL)
    return NULL;

  for (p = values; p->name != NULL; p++) {
    if (strcmp(p->name, XNQueryInputStyle) == 0) {
      if ((styles = (XIMStyles *)Xmalloc(sizeof(XIMStyles) +
		 sizeof(XIMStyle) * p_style->count_styles)) == NULL) {
	break;
      }
      styles->count_styles = p_style->count_styles;
      styles->supported_styles = (XIMStyle *)(&styles[1]);
      for (i = 0; i < (int)(styles->count_styles); i++) {
	styles->supported_styles[i] = p_style->supported_styles[i];
      }
      value = (XIMStyles **)p->value;
      *value = styles;
    } else {
      break;
    }
  }
  return (p->name);
}

static SwitchMode
SwitchFilter(XicCommon ic, XEvent *ev) {
  DefTree *p = NULL;
  unsigned int mod_state;
  KeySym keysym;
  char buf[32];

  if (ev->type != KeyPress) return (False);
  if (ev->xkey.keycode == XIM_COMPOSE_KEYCODE) return (False);

  (void)XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &keysym, 0);

  mod_state = ev->xkey.state;
  mod_state &= ~(ShiftMask | LockMask);

  if (USE_KEYCODEINFO(((XimCommon)ic->core.im)->local_impart->top_state)) {
    for (p = ic->local_icpart->context; p; p = p->next) {
      if (p->keycode != 0) {
	if (((mod_state & p->modifier_mask) == p->modifier) &&
	    (ev->xkey.keycode == p->keycode) &&
	    (keysym == p->keysym)) {
	  /* matched */
	  return Switch_ON;
	}
      }
    }
  }
  if (p == NULL) { /* Unmatched */
    for (p = ic->local_icpart->context; p; p = p->next) {
      if (p->keycode == 0) {
	if (((mod_state & p->modifier_mask) == p->modifier) &&
	    (keysym == p->keysym)) {
	  return Switch_ON;
	}
      }
    }
  }
  return Switch_NOP;
}

Bool
Ximp_Local_KeyFilter(d, w, ev, client_data)
     Display *d;
     Window w;
     XEvent *ev;
     XPointer client_data;
{
  XicCommon ic = (XicCommon)client_data;
  KeySym keysym;
  static char buf[128];
  static XComposeStatus	status;
  unsigned int	  mod_state;
  LocalIMState *prev_state;

  XimCommon im;
  Bool useUnicode;

#ifdef BACKTRACK_WHEN_UNMATCHED
  static long must_be_through = 0;
#endif

  DefTree *p = NULL;
  Bool returnflag = False;

  TRACE_MESSAGE('k', ("local_Filter\n"));

  if (!ic) {
    return False;
  }
  im = (XimCommon)ic->core.im;

  if (im == 0) {
    /* perhaps XIM instance is aleady closed, and set to NULL */
    _XUnregisterFilter(d, w, Ximp_Local_KeyFilter, (XPointer)ic);
    return False;
  }

  if (ev->type != KeyPress) return (False);
  if (ev->xkey.keycode == XIM_COMPOSE_KEYCODE) return (False);
  if (ic->local_icpart == 0) {
    /* most likely associated ic has been already destroyed */
    _XUnregisterFilter(d, w, Ximp_Local_KeyFilter, (XPointer)ic);
    return False;
  }

  if (((XimCommon)ic->core.im)->local_impart == 0) {
    _XUnregisterFilter(d, w, Ximp_Local_KeyFilter, (XPointer)ic);
    return False;
  }

  if (((XimCommon)ic->core.im)->local_impart->top_state == (LocalIMState *)NULL)
    return (False);
#ifdef BACKTRACK_WHEN_UNMATCHED
  if (must_be_through) {
    must_be_through--;
    return (False);
  }
#endif

  (void)memset((void *)(&status), 0, sizeof(XComposeStatus));
  (void)XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &keysym, &status);
  mod_state = ev->xkey.state;
  mod_state &= ~(ShiftMask | LockMask | Mod2Mask );

  if (IsModifierKey(keysym)) return (False);

    /* use binary table */
  if(((XimCommon)ic->core.im)->local_impart->use_binary_table == True){
    XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;
    CARD32	*s_addr = lim->state_addr;
    CARD32	*d_addr = lim->def_addr;
    CARD32	index = 0;
    if(BCF_STATE_USE_KEYCODE(s_addr, (CARD32)(lim->top_state))){
      index = (CARD32)(ic->local_icpart->context);
      while(index){
	if(BCF_DEF_KEYCODE(d_addr, index) != 0){
	  if(((mod_state & BCF_DEF_MODIFIER_MASK(d_addr, index)) == BCF_DEF_MODIFIER(d_addr, index)) &&
	     (ev->xkey.keycode == BCF_DEF_KEYCODE(d_addr, index)) &&
	     (keysym == BCF_DEF_KEYSYM(d_addr, index))){
	    break;
	  }
	}
	index = BCF_DEF_NEXT(d_addr, index);
      }
    }
    if(index == 0){ /* Unmatched */
      index = (CARD32)(ic->local_icpart->context);
      while(index){
	if(BCF_DEF_KEYCODE(d_addr, index) == 0){
	  if(((mod_state & BCF_DEF_MODIFIER_MASK(d_addr, index)) == BCF_DEF_MODIFIER(d_addr, index)) &&
	     (keysym == BCF_DEF_KEYSYM(d_addr, index))){
	    break;
	  }
	}
	index = BCF_DEF_NEXT(d_addr, index);
      }
    }

    if(index){ /* Matched */
      if(BCF_DEF_SUCCESSION(d_addr, index)){ /* Intermediate */
	ic->local_icpart->context = (DefTree *)BCF_DEF_SUCCESSION(d_addr, index);
#ifdef BACKTRACK_WHEN_UNMATCHED
	/* Seve Event */
	bcopy(ev, &ic->ximp_icpart.saved_event[ic->ximp_icpart.num_saved++], sizeof(XKeyEvent));
#endif
      }
      else{ /* Terminate (reached to leaf) */
	if(BCF_DEF_TARGET(d_addr, index) == 0){
	  ic->local_icpart->composed = (DefTree *)
	    Xmalloc(sizeof(DefTree));
	  if (ic->local_icpart->composed == (DefTree *)NULL)
	    return(False);
	  memset(ic->local_icpart->composed, 0, sizeof(DefTree));
	  bcopy((DefTree *)p, ic->local_icpart->composed, sizeof(DefTree));
	  if (((DefTree *)p)->mb != NULL) {
	    int l;
	    l = strlen(p->mb);
	    if (l > 0) {
	      ic->local_icpart->composed->mb = (char *)Xmalloc(l+1);
	      bcopy(((DefTree *)p)->mb, ic->local_icpart->composed->mb, l);
	      ic->local_icpart->composed->mb[l] = '\0';
	    }
	  }

	  useUnicode = (XIM_USE_UNICODE(im) &&
			NULL != ic->commit_string_callback.callback);

	  /* private XIC extension */
	  if (useUnicode) {
	      XIMCallback *cb = &ic->commit_string_callback;
	      XIMUnicodeText cbtext;
	      size_t len = 256;	/* enough */
	      char buffer[256];
	      memset(&cbtext, 0, sizeof(XIMUnicodeText));
	      cbtext.string.multi_byte = buffer;
	      cbtext.length = len;
	      IMConvertFromUTF8ToUTF16(p->mb, strlen(p->mb),
				       (char**)&cbtext.string.utf16_char,
				       &len);
	      cbtext.length -= len;
	      cbtext.length /= sizeof(unsigned short);
	      (*cb->callback)((XIC)ic,
			      cb->client_data,
			      (XPointer)&cbtext);

	  } else {
	    /* return back to client KeyPressEvent keycode == 0 */
	    ev->xkey.keycode = XIM_COMPOSE_KEYCODE;
	    XPutBackEvent(d, ev);
	  }
	}
	else { /* Change state */
	  ic->local_icpart->imstate = (LocalIMState *)BCF_DEF_TARGET(d_addr, index);
	  Ximp_Local_Status_Set(ic);
	  Ximp_Local_Status_Start(ic);
	  Ximp_Local_Status_Draw(ic);
	}
	/* initialize internal state for next key sequence */
	ic->local_icpart->context = (DefTree *)BCF_STATE_PARSER(s_addr, (CARD32)(ic->local_icpart->imstate));
      }
      return (True);
    }
    else{ /* Unmatched */
      if (ic->local_icpart->context == (DefTree *)BCF_STATE_PARSER(s_addr, (CARD32)(ic->local_icpart->imstate))){
	return (False);
      }
      /* Error (Sequence Unmatch occured) */
#ifdef BACKTRACK_WHEN_UNMATCHED
      XPutBackEvent(d, ev);
      while (ic->ximp_icpart.num_saved > 0) {
	ic->ximp_icpart.num_saved--;
	XPutBackEvent(d, &ic->ximp_icpart.saved_event[ic->ximp_icpart.num_saved]);
      }
      must_be_through = 1;
#endif
      /* initialize internal state for next key sequence */
      ic->local_icpart->context = (DefTree *)BCF_STATE_PARSER(s_addr, (CARD32)(ic->local_icpart->imstate));

      return (True);
    }
  }

  /* use text compose file */
  else{
    TRACE_MESSAGE('k', ("0x%08x 0x%08x\n", ic->local_icpart->imstate,
			((XimCommon)ic->core.im)->local_impart->top_state));
    if (USE_KEYCODEINFO(((XimCommon)ic->core.im)->local_impart->top_state)) {
      for (p = ic->local_icpart->context; p; p = p->next) {
	if (p->keycode != 0) {
	  if (((mod_state & p->modifier_mask) == p->modifier) &&
	      (ev->xkey.keycode == p->keycode) &&
	      (keysym == p->keysym)) {
	    break;
	  }
	}
      }
    }
    if (p == NULL) { /* Unmatched */
      for (p = ic->local_icpart->context; p; p = p->next) {
	if (p->keycode == 0) {
	  if (((mod_state & p->modifier_mask) == p->modifier) &&
	      (keysym == p->keysym)) {
	    break;
	  }
	}
      }
    }
    if (p) { /* Matched */
      if (p->succession) { /* Intermediate */
	ic->local_icpart->context = p->succession;
#ifdef BACKTRACK_WHEN_UNMATCHED
	/* Seve Event */
	bcopy(ev, &ic->ximp_icpart.saved_event[ic->ximp_icpart.num_saved++], sizeof(XKeyEvent));
#endif
	returnflag = True;
      } else { /* Terminate (reached to leaf) */
	prev_state = ic->local_icpart->imstate;
	if (prev_state->type == RemoteIMState) {
	  return False;
	}
	if (p->target_name == NULL){
	  switch (p->action) {
	  case TopPage:
	  case LastPage:
	  case NextPage:
	  case PrevPage:
	  case UpCand:
	  case DownCand:
	  case NextCand:
	  case PrevCand:
	  case SelectCand:
	    /* only for lookup */
	    Ximp_Local_Lookup_Action_Filter(ic, p->action, ev);
	    return(True);
	  case DoConv:
	    if (prev_state->type == CodeInputState) {
	      Ximp_Local_Preedit_Conv(ic, ev);
	      return(True);
	    }
	  case DoLookup:
	    if (prev_state->type == CodeInputState) {
	      Ximp_Local_Preedit_Lookup(ic, ev);
	      return(True);
	    }
	    break;
	  case NoAction:
	    /* create a copy of Compose key, BugID: 4157565 */
	    ic->local_icpart->composed = (DefTree *)Xmalloc(sizeof(DefTree));
	    if (ic->local_icpart->composed == (DefTree *)NULL)
	      return(False);
	    memset(ic->local_icpart->composed, 0, sizeof(DefTree));
	    bcopy((DefTree *)p, ic->local_icpart->composed, sizeof(DefTree));
	    if (((DefTree *)p)->mb != NULL) {
	      int l;
	      l = strlen(p->mb);
	      if (l > 0) {
		ic->local_icpart->composed->mb = (char *)Xmalloc(l+1);
		bcopy(((DefTree *)p)->mb, ic->local_icpart->composed->mb, l);
		ic->local_icpart->composed->mb[l] = '\0';
	      }
	    }

	    useUnicode = (XIM_USE_UNICODE(im) &&
			  NULL != ic->commit_string_callback.callback);
	    /* private XIC extension */
	    if (useUnicode) {
	      XIMCallback *cb = &ic->commit_string_callback;
	      XIMUnicodeText cbtext;
	      size_t len = 256;	/* enough */
	      char buffer[256];
	      memset(&cbtext, 0, sizeof(XIMUnicodeText));
	      cbtext.string.multi_byte = buffer;
	      cbtext.length = len;
	      IMConvertFromUTF8ToUTF16(p->mb, strlen(p->mb),
				       (char**)&cbtext.string.utf16_char,
				       &len);
	      cbtext.length -= len;
	      cbtext.length /= sizeof(unsigned short);
	      (*cb->callback)((XIC)ic,
			      cb->client_data,
			      (XPointer)&cbtext);

	    } else {
	      /* return back to client KeyPressEvent keycode == 0 */
	      ev->xkey.keycode = XIM_COMPOSE_KEYCODE;
	      XPutBackEvent(d, ev);
	    }
	    break;
	  default:
	    break;
	  }
	} else {
	  TRACE_MESSAGE('k', ("local_Filter: %s\n", p->target_name));
	  ic->local_icpart->imstate = p->target;
	  ic->local_icpart->context = ic->local_icpart->imstate->parser;
	  if (prev_state->type == CodeInputState) {
	    Ximp_Local_Preedit_Done(ic);
	  } else if (prev_state->type == LookupState) {
	    Ximp_Local_Table_Done(ic);
	  }
          switch (ic->local_icpart->imstate->type) {
	  case RemoteIMState:
	    if (!SwitchRemoteIMState(ic,
				     ic->local_icpart->imstate->language)) {
	      ic->local_icpart->imstate = prev_state;
	      ic->local_icpart->context = ic->local_icpart->imstate->parser;
	      return False;
	    }
	    ic->local_icpart->imstate = ((XimCommon)ic->core.im)->local_impart->top_state;
	    ic->local_icpart->context = ic->local_icpart->imstate->parser;
	    break;
	  case CodeInputState:
	  case LookupState:
	  default:
	    Ximp_Local_Status_Set(ic);
	    Ximp_Local_Status_Start(ic);
	    Ximp_Local_Status_Draw(ic);
	    if (ic->local_icpart->imstate->type == CodeInputState) {
	      Ximp_Local_Preedit_Start(ic);
	    }
	    if (ic->local_icpart->imstate->type == LookupState) {
	      Ximp_Local_Table_Start(ic);
	    }
	    break;
	  }
	}
	/* initialize internal state for next key sequence */
       ic->local_icpart->context = ic->local_icpart->imstate->parser;
#ifdef BACKTRACK_WHEN_UNMATCHED
	ic->ximp_icpart.num_saved = 0;
#endif
      }
      return (True);

    } else { /* Unmatched */
      if (ic->local_icpart->context == ic->local_icpart->imstate->parser) {
	returnflag = False;
      }
      /* Error (Sequence Unmatch occured) */
#ifdef BACKTRACK_WHEN_UNMATCHED
      XPutBackEvent(d, ev);
      while (ic->ximp_icpart.num_saved > 0) {
	ic->ximp_icpart.num_saved--;
	XPutBackEvent(d, &ic->ximp_icpart.saved_event[ic->ximp_icpart.num_saved]);
      }
      must_be_through = 1;
#endif
      /* initialize internal state for next key sequence */
      ic->local_icpart->context = ic->local_icpart->imstate->parser;
      returnflag = False;
    }
  }

  if(ic->local_icpart->imstate->type == CodeInputState) {
    LocalPreeditExt *hook = (LocalPreeditExt *)(ic->local_icpart->preedit_ext);
    LocalIMState *state = (LocalIMState *)(ic->local_icpart->imstate);
    LocalLookupExt *lookup = (LocalLookupExt *)(ic->local_icpart->lookup_ext);
    int max_len = state->attr->_attr_u.CIstruct.max_input_len;

    returnflag = True;
    if (lookup && lookup->mapped) {
      Ximp_Local_Lookup_Input_Filter(ic, buf, ev);
    } else {
      if (Ximp_Local_Preedit_Draw(ic, keysym)) {
	if (hook && hook->preedit_len >= max_len) {
	  Ximp_Local_Preedit_Conv(ic, ev);
	}
      } else {
	returnflag = False;
      }
    }
  } else if (ic->local_icpart->imstate->type == LookupState) {
    LocalLookupExt *lookup = (LocalLookupExt *)(ic->local_icpart->lookup_ext);
    if (lookup && lookup->mapped) {
      returnflag = Ximp_Local_Lookup_Input_Filter(ic, buf, ev);
    }
    Ximp_Local_Table_Draw(ic);
  }
  return(returnflag);
}

static XIC
_Ximp_Local_CreateIC(XIM im, XIMArg *arg) {
  XicCommon ic = (XicCommon)NULL;

  ic = (XicCommon)Xmalloc(sizeof(XicCommonRec));
  if (ic == (XicCommon)NULL) goto Error;
  memset(ic, 0, sizeof(XicCommonRec));

  ic->core.im = (XIM)im;
  ic->methods = get_local_ic_methods();

  if (!CommonCreateIC((XIC)ic, arg)) goto Error;
    
  if (!COMPOSE_CreateIC_SWITCH((XIC)ic, arg)) goto Error;

  _XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			 KeyPress, KeyRelease,
			 Ximp_Local_KeyFilter, (XPointer)ic);
  return (XIC)ic;
 Error:
  if (ic) Xfree(ic);
  return 0;
}
Status
COMPOSE_CreateIC_SWITCH(XIC xic, XIMArg *arg) {
  XicCommon ic = (XicCommon)xic;
  XimCommon im = (XimCommon)ic->core.im;
  XimpChangeMaskRec dummy;

  if ((ic->local_icpart = (XICComposeIM)Xmalloc(sizeof(XICComposeIMRec)))
      == (XICComposeIM)NULL) {
    goto Error;
  }
  memset(ic->local_icpart, 0, sizeof(XICComposeIMRec));

  XIC_COMPOSE(ic, switch_methods) = get_switch_ic_methods();

  ic->core.filter_events = KeyPressMask;

  if (XIM_COMPOSE(im, top_state)) {
    if (XIM_COMPOSE(im, use_binary_table) == True)
      ic->local_icpart->context =
	(DefTree *)BCF_STATE_PARSER(XIM_COMPOSE(im, state_addr),
				    (CARD32)XIM_COMPOSE(im, top_state));
    else
      ic->local_icpart->context = XIM_COMPOSE(im, top_state)->parser;
  }
  ic->local_icpart->composed = NULL;
  ic->local_icpart->imstate = ((XimCommon)im)->local_impart->top_state;

  if (!ic->local_icpart->imstate) {
    fprintf(stderr, "The local IM state map is empty.  Compose file may be broken.\n");
    goto Error;
  }

  memset(&dummy, 0, sizeof(dummy));
  if (_Ximp_Local_SetICValueData((Ximp_XIC)ic, arg, XIMP_CREATE_IC, &dummy)) {
    goto Error;
  }
  /* Not Yet */
  if (!(ic->ximp_icpart->value_mask & XIMP_INPUT_STYLE)) 
    goto Error;

  ic->ximp_icpart->input_mode = BEING_BYPASSED;
  Ximp_Local_Status_Set(ic);
  Ximp_Local_Status_Start(ic);
  Ximp_Local_Status_Draw(ic);

  /* switch */
  if (XIM_IS_SWITCH(im)) {
    RegisterSwitchFilter(ic, SwitchFilter,
			 Ximp_Local_KeyFilter,
			 get_local_ic_methods());
  }
  return True;

 Error:
  if (ic->local_icpart) XFree(ic->local_icpart);
  return False;
}

static Bool
_Ximp_StatusGetAttributes(ic, vl, return_name)
    Ximp_XIC		ic;
    XIMArg	 	*vl;
    char		**return_name;
{
    XIMArg		*p;
    XRectangle	*p_rect;
    XIMCallback 	*p_callback;

  if (((XimCommon)ic->core.im)->local_impart == NULL)
    return True;

  for(p = vl; p->name != NULL; p++) {
	if(strcmp(p->name, XNArea)==0) {
	    if(XIMP_CHK_STSAREAMASK(ic)) {
		if((p_rect = (XRectangle *)Xmalloc(sizeof(XRectangle))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_rect->x       = ic->core.status_attr.area.x;
		p_rect->y       = ic->core.status_attr.area.y;
		p_rect->width   = ic->core.status_attr.area.width;
		p_rect->height  = ic->core.status_attr.area.height;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	    *((XRectangle **)(p->value)) = p_rect;
	} else if(strcmp(p->name, XNAreaNeeded)==0) {
	  unsigned int win_width, win_height;
	  XFontSet fs = ic->core.status_attr.fontset;
	  XFontSetExtents *fse = 0;

	  if (!fs || !XIMP_CHK_STSFONTMASK(ic)) {
	    SetStatusFont((XicCommon)ic, NULL);
	    fs = ic->core.status_attr.fontset;
	  }

	  if((p_rect = (XRectangle *)Xmalloc(sizeof(XRectangle))) ==
	     (XRectangle *)NULL) {
	    *return_name = p->name;
	    return(False);
	  }
	  memset(p_rect, 0, sizeof(XRectangle));
	  p_rect->x  = p_rect->y  = 0;

	  if (fs) fse = XExtentsOfFontSet(fs);
	  if (fse && fse->max_logical_extent.width)
	    win_width = fse->max_logical_extent.width * MAX_STATUS_CHARNUM * 4;
	  else
	    win_width = 400;
	  if (fse && fse->max_logical_extent.height)
	    win_height = fse->max_logical_extent.height;
	  else
	    win_height = 20;

	  p_rect->width = ic->core.status_attr.area_needed.width;
	  if (!(p_rect->width) || (p_rect->width > win_width)) {
	    p_rect->width = win_width;
	  }

	  p_rect->height = ic->core.status_attr.area_needed.height;
	  if (!(p_rect->height) || (p_rect->height > win_height)) {
	    p_rect->height = win_height;
	  }
	  *((XRectangle **)(p->value)) = p_rect;
	} else if(strcmp(p->name, XNColormap)==0) {
	    if(XIMP_CHK_STSCOLORMAPMASK(ic)) {
		*((Colormap *)(p->value)) = ic->core.status_attr.colormap;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNStdColormap)==0) {
	    if(XIMP_CHK_STSSTDCOLORMAPMASK(ic)) {
		*((Atom *)(p->value)) = ic->core.status_attr.std_colormap;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNBackground)==0) {
	    if(XIMP_CHK_STSBGMASK(ic)) {
		*((unsigned long *)(p->value)) = ic->core.status_attr.background;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNForeground)==0) {
	    if(XIMP_CHK_STSFGMASK(ic)) {
		*((unsigned long *)(p->value)) = ic->core.status_attr.foreground;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNBackgroundPixmap)==0) {
	    if(XIMP_CHK_STSBGPIXMAPMASK(ic)) {
		*((Pixmap *)(p->value)) = ic->core.status_attr.background_pixmap;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNFontSet)==0) {
	    if(XIMP_CHK_STSFONTMASK(ic)) {
		*((XFontSet *)(p->value)) = ic->core.status_attr.fontset;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNLineSpace)==0) {
	    if(XIMP_CHK_STSLINESPMASK(ic)) {
		*((int *)(p->value)) = ic->core.status_attr.line_spacing;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNCursor)==0) {
	    if(XIMP_CHK_STSCURSORMASK(ic)) {
		*((Cursor *)(p->value)) = ic->core.status_attr.cursor;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNStatusStartCallback)==0) {
	    if((int)ic->core.status_attr.start_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.status_attr.start_callback.client_data;
		p_callback->callback =
		    ic->core.status_attr.start_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNStatusDrawCallback)==0) {
	    if((int)ic->core.status_attr.draw_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.status_attr.draw_callback.client_data;
		p_callback->callback =
		    ic->core.status_attr.draw_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNStatusDoneCallback)==0) {
	    if((int)ic->core.status_attr.done_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.status_attr.done_callback.client_data;
		p_callback->callback =
		    ic->core.status_attr.done_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	}
    }
    return(True);
}

static Bool
_Ximp_PreGetAttributes(ic, vl, return_name)
Ximp_XIC	 ic;
XIMArg		*vl;
char		**return_name;
{
    XIMArg		*p;
    XRectangle	*p_rect;
    XPoint		*p_point;
    XIMCallback 	*p_callback;

    if (((XimCommon)ic->core.im)->local_impart == NULL)
      return True;

    for(p = vl; p->name != NULL; p++) {
	if(strcmp(p->name, XNArea)==0) {
	    if(XIMP_CHK_PREAREAMASK(ic)) {
		if((p_rect = (XRectangle *)Xmalloc(sizeof(XRectangle))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_rect->x       = ic->core.preedit_attr.area.x;
		p_rect->y       = ic->core.preedit_attr.area.y;
		p_rect->width   = ic->core.preedit_attr.area.width;
		p_rect->height  = ic->core.preedit_attr.area.height;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	    *((XRectangle **)(p->value)) = p_rect;
	} else if(strcmp(p->name, XNAreaNeeded)==0) {
	  unsigned int win_width, win_height;
	  XFontSet fs = ic->core.preedit_attr.fontset;
	  XFontSetExtents *fse = 0;

	  if((p_rect = (XRectangle *)Xmalloc(sizeof(XRectangle))) ==
	     (XRectangle *)NULL) {
	    *return_name = p->name;
	    return(False);
	  }
	  memset(p_rect, 0, sizeof(XRectangle));
	  p_rect->x  = p_rect->y  = 0;

	  if (fs) fse = XExtentsOfFontSet(fs);
	  if (fse && fse->max_logical_extent.width)
	    win_width = fse->max_logical_extent.width * MAX_PREEDIT_CHARNUM;
	  else
	    win_width = 100;
	  if (fse && fse->max_logical_extent.height)
	    win_height = fse->max_logical_extent.height;
	  else
	    win_height = 20;

	  p_rect->width = ic->core.preedit_attr.area_needed.width;
	  if (!(p_rect->width) || (p_rect->width > win_width)) {
	    p_rect->width = win_width;
	  }

	  p_rect->height = ic->core.preedit_attr.area_needed.height;
	  if (!(p_rect->height) || (p_rect->height > win_height)) {
	    p_rect->height = win_height;
	  }
	  *((XRectangle **)(p->value)) = p_rect;
	} else if(strcmp(p->name, XNSpotLocation)==0) {
	    if(XIMP_CHK_PRESPOTLMASK(ic)) {
		if((p_point = (XPoint *)Xmalloc(sizeof(XPoint))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_point->x = ic->core.preedit_attr.spot_location.x;
		p_point->y = ic->core.preedit_attr.spot_location.y;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	    *((XPoint **)(p->value)) = p_point;
	} else if(strcmp(p->name, XNColormap)==0) {
	    if(XIMP_CHK_PRECOLORMAPMASK(ic)) {
		*((Colormap *)(p->value)) = ic->core.preedit_attr.colormap;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNStdColormap)==0) {
	    if(XIMP_CHK_PRESTDCOLORMAPMASK(ic))
		*((Atom *)(p->value)) = ic->core.preedit_attr.std_colormap;
	    else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNBackground)==0) {
	    if(XIMP_CHK_PREBGMASK(ic)) {
		*((unsigned long *)(p->value)) = ic->core.preedit_attr.background;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNForeground)==0) {
	    if(XIMP_CHK_PREFGMASK(ic)) {
		*((unsigned long *)(p->value)) = ic->core.preedit_attr.foreground;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNBackgroundPixmap)==0) {
	    if(XIMP_CHK_PREBGPIXMAPMASK(ic)) {
		*((Pixmap *)(p->value)) = ic->core.preedit_attr.background_pixmap;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNFontSet)==0) {
	    if(XIMP_CHK_PREFONTMASK(ic)) {
		*((XFontSet *)(p->value)) = ic->core.preedit_attr.fontset;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNLineSpace)==0) {
	    if(XIMP_CHK_PRELINESPMASK(ic)) {
		*((int *)(p->value)) = ic->core.preedit_attr.line_spacing;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNCursor)==0) {
	    if(XIMP_CHK_PRECURSORMASK(ic)) {
		*((Cursor *)(p->value)) = ic->core.preedit_attr.cursor;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNPreeditStartCallback)==0) {
	    if((int)ic->core.preedit_attr.start_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.preedit_attr.start_callback.client_data;
		p_callback->callback =
		    ic->core.preedit_attr.start_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNPreeditDrawCallback)==0) {
	    if((int)ic->core.preedit_attr.draw_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.preedit_attr.draw_callback.client_data;
		p_callback->callback =
		    ic->core.preedit_attr.draw_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNPreeditDoneCallback)==0) {
	    if((int)ic->core.preedit_attr.done_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.preedit_attr.done_callback.client_data;
		p_callback->callback =
		    ic->core.preedit_attr.done_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNPreeditCaretCallback)==0) {
	    if((int)ic->core.preedit_attr.caret_callback.callback) {
		if((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
		    *return_name = p->name;
		    return(False);
		}
		p_callback->client_data =
		    ic->core.preedit_attr.caret_callback.client_data;
		p_callback->callback =
		    ic->core.preedit_attr.caret_callback.callback;
		*((XIMCallback **)(p->value)) = p_callback;
	    } else {
		*return_name = p->name;
		return(False);
	    }
	} else if(strcmp(p->name, XNPreeditState)==0) {
	  *((XIMPreeditState*)(p->value)) = ic->core.preedit_attr.preedit_state;
	} else if(strcmp(p->name, XNPreeditStateNotifyCallback)==0) {
	  if ((int)ic->core.preedit_attr.state_notify_callback.callback) {
	    if ((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
	      *return_name = p->name;
	      return(False);
	    }
	    p_callback->client_data =
	      ic->core.preedit_attr.state_notify_callback.client_data;
	    p_callback->callback =
	      ic->core.preedit_attr.state_notify_callback.callback;
	    *((XIMCallback **)(p->value)) = p_callback;
	  } else {
	    *return_name = p->name;
	    return(False);
	  }
	}
    }
    return(True);
}

static char *
_Ximp_Local_GetICValues(XIC xic, XIMArg *arg) {
    XicCommon ic = (XicCommon)xic;
    XIMArg		*p;
    char		*p_char;
    char		*return_name = NULL;
    int		 len;

    if (((XimCommon)ic->core.im)->local_impart == NULL)
      return NULL;

    for(p = arg; p->name != NULL; p++) {
	if(strcmp(p->name, XNInputStyle) == 0) {
	    if(ic->ximp_icpart->value_mask & XIMP_INPUT_STYLE) {
		*((XIMStyle *)(p->value)) = ic->core.input_style;
	    } else {
		return_name = p->name;
		break;
	    }
	} else if(strcmp(p->name, XNClientWindow)==0) {
	    if(ic->ximp_icpart->value_mask & XIMP_CLIENT_WIN) {
		*((Window *)(p->value)) = ic->core.client_window;
	    } else {
		return_name = p->name;
		break;
	    }
	} else if(strcmp(p->name, XNFocusWindow)==0) {
	    if(XIMP_CHK_FOCUSWINMASK(ic)) {
		*((Window *)(p->value)) = ic->core.focus_window;
	    } else {
		return_name = p->name;
		break;
	    }
	} else if(strcmp(p->name, XNResourceName)==0) {
	    if(ic->core.im->core.res_name != (char *)NULL) {
		    len = strlen(ic->core.im->core.res_name);
		if((p_char = Xmalloc(len+1)) == NULL) {
		    return_name = p->name;
		    break;
		}
		strcpy(p_char, ic->core.im->core.res_name);
		*((char **)(p->value)) = p_char;
	    } else {
		return_name = p->name;
		break;
	    }
	} else if(strcmp(p->name, XNResourceClass)==0) {
	    if(ic->core.im->core.res_class != (char *)NULL) {
		len = strlen(ic->core.im->core.res_class);
		if((p_char = Xmalloc(len+1)) == NULL) {
		    return_name = p->name;
		    break;
		}
		strcpy(p_char, ic->core.im->core.res_class);
		*((char **)(p->value)) = p_char;
	    } else {
		return_name = p->name;
		break;
	    }
	} else if(strcmp(p->name, XNGeometryCallback)==0) {
	    if(ic->ximp_icpart->value_mask & XIMP_GEOMETRY_CB) {
		*((XICCallback *)(p->value)) = ic->core.geometry_callback;
	    } else {
		return_name = p->name;
		break;
	    }
	} else if(strcmp(p->name, XNFilterEvents)==0) {
	    *((unsigned long *)(p->value)) = ic->core.filter_events;
	} else if(strcmp(p->name, XNPreeditAttributes)==0) {
	    if( _Ximp_PreGetAttributes((Ximp_XIC)ic, (XIMArg*)p->value,
				       &return_name) == False)
		break;
	} else if(strcmp(p->name, XNStatusAttributes)==0) {
	    if( _Ximp_StatusGetAttributes((Ximp_XIC)ic, (XIMArg*)p->value,
					  &return_name) == False)
		break;
	} else if (strcmp(p->name, XNPreeditState)==0) { 
	  *((XIMPreeditState*)(p->value)) = ic->core.preedit_attr.preedit_state;
	} else if (strcmp(p->name, XNPreeditStateNotifyCallback)==0) { 
	  XIMCallback *p_callback;
	  if ((int)ic->core.preedit_attr.state_notify_callback.callback) {
	    if ((p_callback = (XIMCallback *)Xmalloc(sizeof(XIMCallback))) == NULL) {
	      return_name = p->name;
	      return(False);
	    }
	    p_callback->client_data =
	      ic->core.preedit_attr.state_notify_callback.client_data;
	    p_callback->callback =
	      ic->core.preedit_attr.state_notify_callback.callback;
	    *((XIMCallback **)(p->value)) = p_callback;
	  } else {
	    return_name = p->name;
	    break;
	  }
	}
    }
    return(return_name);
}

static void
_Ximp_Local_DestroyIC(xic)
XIC xic;
{
  SWITCH_DestroyIC(xic);
  CommonDestroyIC(xic);
  return;
}

static void
_Ximp_Local_SetFocus(XIC xic) {
  XicCommon ic = (XicCommon)xic;
  XIC current_ic;

  if (((XimCommon)ic->core.im)->local_impart == NULL)
    return;

  current_ic = ((XimCommon)ic->core.im)->local_impart->current_ic;

  if (current_ic != (XIC)NULL) {
    _Ximp_Local_UnSetFocus((XIC)current_ic);
  }
  ((XimCommon)ic->core.im)->local_impart->current_ic = (XIC)ic;

  if (!XIM_IS_SWITCH(ic->core.im))
    _XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			   KeyPress, KeyRelease,
			   Ximp_Local_KeyFilter, (XPointer)ic);
  Ximp_Local_Status_Set(ic);
  Ximp_Local_Status_Start(ic);
  Ximp_Local_Status_Draw(ic);
  return;
}

static void
_Ximp_Local_UnSetFocus(XIC xic) {
  XicCommon ic = (XicCommon)xic;

  if (((XimCommon)ic->core.im)->local_impart == NULL)
    return;

  ((XimCommon)ic->core.im)->local_impart->current_ic = (XIC)NULL;
  _XUnregisterFilter(ic->core.im->core.display, ic->core.focus_window,
		     Ximp_Local_KeyFilter, (XPointer)ic);
}

static Bool
_Ximp_PreSetAttributes(ic, attr, vl, mode, change_mask, return_name)
Ximp_XIC		 ic;
Ximp_PreeditPropRec4	*attr;
XIMArg			*vl;
int			 mode;
XimpChangeaMask 	 change_mask;
char			*return_name;
{
	XIMArg			*p;
	XStandardColormap	*colormap_ret;
	int			 list_ret;
	XFontStruct		**struct_list;
	char			**name_list;
	int 			 i, len;
	int			 count;
	char 			*tmp;

	for(p = vl; p && p->name != NULL; p++) {
		TRACE_MESSAGE('a', ("local_PreSetAttributes: %s\n", p->name));
		if(strcmp(p->name, XNArea)==0) {
			ic->core.preedit_attr.area.x = ((XRectangle *)p->value)->x;
			ic->core.preedit_attr.area.y = ((XRectangle *)p->value)->y;
			ic->core.preedit_attr.area.width = ((XRectangle *)p->value)->width;
			ic->core.preedit_attr.area.height = ((XRectangle *)p->value)->height;
			attr->Area.x      = ic->core.preedit_attr.area.x;
			attr->Area.y      = ic->core.preedit_attr.area.y;
			attr->Area.width  = ic->core.preedit_attr.area.width;
			attr->Area.height = ic->core.preedit_attr.area.height;
			XIMP_SET_PREAREAMASK(ic, change_mask);

		} else if(strcmp(p->name, XNAreaNeeded)==0) {
			ic->core.preedit_attr.area_needed.width  = ((XRectangle *)p->value)->width;
			ic->core.preedit_attr.area_needed.height = ((XRectangle *)p->value)->height;
			attr->AreaNeeded.width  = ic->core.preedit_attr.area_needed.width;
			attr->AreaNeeded.height = ic->core.preedit_attr.area_needed.height;
			XIMP_SET_PREAREANEEDMASK(ic, change_mask);

		} else if(strcmp(p->name, XNSpotLocation)==0) {
			ic->core.preedit_attr.spot_location.x = ((XPoint *)p->value)->x;
			ic->core.preedit_attr.spot_location.y = ((XPoint *)p->value)->y;
			attr->SpotLocation.x = ic->core.preedit_attr.spot_location.x;
			attr->SpotLocation.y = ic->core.preedit_attr.spot_location.y;
			XIMP_SET_PRESPOTLMASK(ic, change_mask);

		} else if(strcmp(p->name, XNColormap)==0) {
			ic->core.preedit_attr.colormap = (Colormap)p->value;
			attr->Colormap = ic->core.preedit_attr.colormap;
			XIMP_SET_PRECOLORMAPMASK(ic, change_mask);

		} else if(strcmp(p->name, XNStdColormap)==0) {
			if( XGetRGBColormaps(ic->core.im->core.display,
					ic->core.focus_window, &colormap_ret,
					&count, (Atom)p->value) != 0) {
				ic->core.preedit_attr.std_colormap = (Atom)p->value;
				attr->StdColormap = ic->core.preedit_attr.std_colormap;
				XIMP_SET_PRESTDCOLORMAPMASK(ic, change_mask);
			} else {
				return_name = p->name;
				return(False);
			}

		} else if(strcmp(p->name, XNBackground)==0) {
			ic->core.preedit_attr.background = (unsigned long)p->value;
			attr->Background = ic->core.preedit_attr.background;
			XIMP_SET_PREBGMASK(ic, change_mask);

		} else if(strcmp(p->name, XNForeground)==0) {
			ic->core.preedit_attr.foreground = (unsigned long)p->value;
			attr->Foreground = ic->core.preedit_attr.foreground;
			XIMP_SET_PREFGMASK(ic, change_mask);

		} else if(strcmp(p->name, XNBackgroundPixmap)==0) {
			ic->core.preedit_attr.background_pixmap = (Pixmap)p->value;
			attr->Bg_Pixmap = ic->core.preedit_attr.background_pixmap;
			XIMP_SET_PREBGPIXMAPMASK(ic, change_mask);

		} else if(strcmp(p->name, XNFontSet)==0) {
		  ic->core.preedit_attr.fontset = (XFontSet)p->value;
		  if(p->value != NULL) {
		    if(ic->ximp_icpart->preedit_font)
		      Xfree(ic->ximp_icpart->preedit_font);
		    list_ret = XFontsOfFontSet(ic->core.preedit_attr.fontset,
					       &struct_list, &name_list);
		    for(i = 0, len = 0; i < list_ret; i++) {
		      len += (strlen(name_list[i]) + sizeof(char));
		    }
		    if( (tmp = Xmalloc(len + list_ret + sizeof(char))) == NULL ) {
		      return_name = p->name;
		      return( False );
		    }
		    tmp[0] = 0;
		    for(i = 0; i < list_ret; i++) {
		      strcat(tmp, name_list[i]);
		      strcat(tmp, ",");
		    }
		    tmp[len + i - 1] = 0;
		    ic->ximp_icpart->preedit_font = tmp;
		    XIMP_SET_PREFONTMASK(ic, change_mask);
		  } else {
		    return_name = p->name;
		    return(False);
		  }

		} else if(strcmp(p->name, XNLineSpace)==0) {
		    ic->core.preedit_attr.line_spacing = (long)p->value;
		    attr->LineSpacing = ic->core.preedit_attr.line_spacing;
		    XIMP_SET_PRELINESPMASK(ic, change_mask);

		} else if(strcmp(p->name, XNCursor)==0) {
		    ic->core.preedit_attr.cursor = (Cursor)p->value;
		    attr->Cursor = ic->core.preedit_attr.cursor;
		    XIMP_SET_PRECURSORMASK(ic, change_mask);

		} else if(strcmp(p->name, XNPreeditStartCallback)==0) {
		    ic->core.preedit_attr.start_callback.client_data =
			((XIMCallback *)p->value)->client_data;
		    ic->core.preedit_attr.start_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_PRE_CALLBAK;

		} else if(strcmp(p->name, XNPreeditDoneCallback)==0) {
		    ic->core.preedit_attr.done_callback.client_data =
			((XIMCallback *)p->value)->client_data;
		    ic->core.preedit_attr.done_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_PRE_CALLBAK;
		    
		} else if(strcmp(p->name, XNPreeditDrawCallback)==0) {
		    ic->core.preedit_attr.draw_callback.client_data =
			((XIMCallback *)p->value)->client_data;
		    ic->core.preedit_attr.draw_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_PRE_CALLBAK;

		} else if(strcmp(p->name, XNPreeditCaretCallback)==0) {
		    ic->core.preedit_attr.caret_callback.client_data =
			((XIMCallback *)p->value)->client_data;
		    ic->core.preedit_attr.caret_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_PRE_CALLBAK;
		} else if(strcmp(p->name, XNPreeditState)==0) {
      		    ic->core.preedit_attr.preedit_state = (XIMPreeditState)p->value;
      	            if (XIM_IS_SWITCH(ic->core.im))
		      ChangePreeditState((XicCommon)ic);
                } else if (strcmp(p->name, XNPreeditStateNotifyCallback)==0) {
      		    ic->core.preedit_attr.state_notify_callback.client_data =
			((XIMCallback *)p->value)->client_data;
      		    ic->core.preedit_attr.state_notify_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_PRE_CALLBAK;
		}
	}
	return(True);
}

static Bool
_Ximp_StatusSetAttributes(ic, attr, vl, mode, change_mask, return_name)
Ximp_XIC		 ic;
Ximp_StatusPropRec4	*attr;
XIMArg			*vl;
int			 mode;
XimpChangeaMask 	 change_mask;
char			*return_name;
{
	XIMArg			*p;
	XStandardColormap 	*colormap_ret;
	int			 list_ret;
	XFontStruct		**struct_list;
	char			**name_list;
	int 			 i, len;
	int			 count;
	char 			*tmp;

	for(p = vl; p && p->name != NULL; p++) {
		if(strcmp(p->name, XNArea)==0) {
			ic->core.status_attr.area.x = ((XRectangle *)p->value)->x;
			ic->core.status_attr.area.y = ((XRectangle *)p->value)->y;
			ic->core.status_attr.area.width = ((XRectangle *)p->value)->width;
			ic->core.status_attr.area.height = ((XRectangle *)p->value)->height;
			attr->Area.x      = ic->core.status_attr.area.x;
			attr->Area.y      = ic->core.status_attr.area.y;
			attr->Area.width  = ic->core.status_attr.area.width;
			attr->Area.height = ic->core.status_attr.area.height;
			XIMP_SET_STSAREAMASK(ic, change_mask);
			Ximp_Local_Status_Window((XicCommon)ic);
		} else if(strcmp(p->name, XNAreaNeeded)==0) {
			ic->core.status_attr.area_needed.width  = ((XRectangle *)p->value)->width;
			ic->core.status_attr.area_needed.height = ((XRectangle *)p->value)->height;
			attr->AreaNeeded.width  = ic->core.status_attr.area_needed.width;
			attr->AreaNeeded.height = ic->core.status_attr.area_needed.height;
			XIMP_SET_STSAREANEEDMASK(ic, change_mask);

		} else if(strcmp(p->name, XNColormap)==0) {
			ic->core.status_attr.colormap = (Colormap)p->value;
			attr->Colormap = ic->core.status_attr.colormap;
			XIMP_SET_STSCOLORMAPMASK(ic, change_mask);

		} else if(strcmp(p->name, XNStdColormap)==0) {
			if(XGetRGBColormaps(ic->core.im->core.display,
					ic->core.focus_window, &colormap_ret,
					&count, (Atom)p->value) !=0) {
				ic->core.status_attr.std_colormap = (Atom)p->value;
				attr->StdColormap = ic->core.status_attr.std_colormap;
				XIMP_SET_STSSTDCOLORMAPMASK(ic, change_mask);
			} else {
				return_name = p->name;
				return(False);
			}

		} else if(strcmp(p->name, XNBackground)==0) {
			ic->core.status_attr.background = (unsigned long)p->value;
			attr->Background = ic->core.status_attr.background;
			XIMP_SET_STSBGMASK(ic, change_mask);
		} else if(strcmp(p->name, XNForeground)==0) {
			ic->core.status_attr.foreground = (unsigned long)p->value;
			attr->Foreground = ic->core.status_attr.foreground;
			XIMP_SET_STSFGMASK(ic, change_mask);
		} else if(strcmp(p->name, XNBackgroundPixmap)==0) {
			ic->core.status_attr.background_pixmap = (Pixmap)p->value;
			attr->Bg_Pixmap = ic->core.status_attr.background_pixmap;
			XIMP_SET_STSBGPIXMAPMASK(ic, change_mask);

		} else if(strcmp(p->name, XNFontSet)==0) {
		  ic->core.status_attr.fontset = (XFontSet)p->value;
		  if (p->value != NULL) {
		    if(ic->ximp_icpart->status_font)
		      Xfree(ic->ximp_icpart->status_font);
		    list_ret = XFontsOfFontSet(ic->core.status_attr.fontset,
					       &struct_list, &name_list);
		    for(i = 0, len = 0; i < list_ret; i++) {
		      len += (strlen(name_list[i]) + sizeof(char));
		    }
		    if((tmp = Xmalloc(len + list_ret + sizeof(char))) == NULL){
		      return_name = p->name;
		      return( False );
		    }
		    tmp[0] = 0;
		    for(i = 0; i < list_ret; i++) {
		      strcat(tmp, name_list[i]);
		      strcat(tmp, ",");
		    }
		    tmp[len + i - 1] = 0;
		    ic->ximp_icpart->status_font = tmp;
		    XIMP_SET_STSFONTMASK(ic, change_mask);
		  } else {
		    return_name = p->name;
		    return(False);
		  }
		} else if(strcmp(p->name, XNLineSpace)==0) {
		    ic->core.status_attr.line_spacing = (long)p->value;
		    attr->LineSpacing = ic->core.status_attr.line_spacing;
		    XIMP_SET_STSLINESPMASK(ic, change_mask);

		} else if(strcmp(p->name, XNCursor)==0) {
			ic->core.status_attr.cursor = (Cursor)p->value;
			attr->Cursor = ic->core.status_attr.cursor;
			XIMP_SET_STSCURSORMASK(ic, change_mask);

		} else if(strcmp(p->name, XNStatusStartCallback)==0) {
		    ic->core.status_attr.start_callback.client_data =
			((XIMCallback *)p->value)->client_data;
			ic->core.status_attr.start_callback.callback =
				((XIMCallback *)p->value)->callback;
			ic->ximp_icpart->value_mask |= XIMP_STS_CALLBAK;

		} else if(strcmp(p->name, XNStatusDoneCallback)==0) {
		    ic->core.status_attr.done_callback.client_data =
				((XIMCallback *)p->value)->client_data;
		    ic->core.status_attr.done_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_STS_CALLBAK;

		} else if(strcmp(p->name, XNStatusDrawCallback)==0) {
		    ic->core.status_attr.draw_callback.client_data =
			((XIMCallback *)p->value)->client_data;
		    ic->core.status_attr.draw_callback.callback =
			((XIMCallback *)p->value)->callback;
		    ic->ximp_icpart->value_mask |= XIMP_STS_CALLBAK;
		}
	}
	return(True);
}

static void ComposeKeyEventFilter(Ximp_XIC ic, Window old_focus_window);

static char *
_Ximp_Local_SetICValueData(Ximp_XIC ic, XIMArg *arg,
			   int mode, XimpChangeaMask change_mask) {
  XIMArg *p;
  char *return_name = NULL;

  for (p = arg; p->name != NULL; p++) {
    if (strcmp(p->name, XNInputStyle) == 0) {
      if (mode == XIMP_CREATE_IC) {
	ic->core.input_style = (XIMStyle)p->value;
	ic->ximp_icpart->value_mask |= XIMP_INPUT_STYLE;
      } else {
	; /* Currently Fixed value */
      }
    } else if(strcmp(p->name, XNClientWindow)==0) {
      if (!(ic->ximp_icpart->value_mask & XIMP_CLIENT_WIN)) {
	ic->core.client_window = (Window)p->value;
	ic->ximp_icpart->value_mask |= XIMP_CLIENT_WIN;
	if (!(XIMP_CHK_FOCUSWINMASK(ic))) {
	  ic->core.focus_window = ic->core.client_window;
	  XIMP_SET_FOCUSWINMASK2(ic, change_mask);
	}
	Ximp_Local_Status_Create((XicCommon)ic);
      } else {
 	if (ic->core.client_window == (Window)p->value)
 	  /* same window */
 	  continue;
 
	return_name = p->name;
	break; /* Can't change this value */
      }
    } else if(strcmp(p->name, XNFocusWindow)==0) {
      Window old_focus_window = 0;
      if ((Window)p->value != 0 &&
	  (Window)p->value == ic->core.focus_window) {
	continue;
      }
      old_focus_window = ic->core.focus_window;
      ic->core.focus_window = (Window)p->value;
      XIMP_SET_FOCUSWINMASK2(ic, change_mask);
      ComposeKeyEventFilter(ic, old_focus_window);
    } else if(strcmp(p->name, XNResourceName)==0) {
      ic->core.im->core.res_name = (char *)p->value;
      ic->ximp_icpart->value_mask |= XIMP_RES_NAME;
	    
    } else if(strcmp(p->name, XNResourceClass)==0) {
      ic->core.im->core.res_class = (char *)p->value;
      ic->ximp_icpart->value_mask |= XIMP_RES_CLASS;
	    
    } else if(strcmp(p->name, XNGeometryCallback)==0) {
      ic->core.geometry_callback.client_data =
	((XICCallback *)p->value)->client_data;
      ic->core.geometry_callback.callback =
	((XICCallback *)p->value)->callback;
      ic->ximp_icpart->value_mask |= XIMP_GEOMETRY_CB;
	    
    } else if(strcmp(p->name, XNPreeditAttributes)==0) {
      if( _Ximp_PreSetAttributes((Ximp_XIC)ic,
				 &(ic->ximp_icpart->preedit_attr),
				 (XIMArg*)p->value, mode, change_mask,
				 return_name) == False )
	break;
	    
    } else if(strcmp(p->name, XNStatusAttributes)==0) {
      if( _Ximp_StatusSetAttributes((Ximp_XIC)ic,
				    &(ic->ximp_icpart->status_attr),
				    (XIMArg*)p->value, mode, change_mask,
				    return_name) == False )
	break;
    } else if (strcmp(p->name, XNPreeditState)==0) {
      ic->core.preedit_attr.preedit_state = (XIMPreeditState)p->value;
      if (XIM_IS_SWITCH(ic->core.im))
 	ChangePreeditState((XicCommon)ic);
    } else if (strcmp(p->name, XNPreeditStateNotifyCallback)==0) {
      ic->core.preedit_attr.state_notify_callback.client_data =
	((XIMCallback *)p->value)->client_data;
      ic->core.preedit_attr.state_notify_callback.callback =
	((XIMCallback *)p->value)->callback;
    } else {
      return_name = p->name;
      break;
    }
  }
  return(return_name);
}

static char *
_Ximp_Local_SetICValues(XIC xic, XIMArg *arg) {
  XicCommon	 ic = (XicCommon)xic;
  char		*ret;
  XimpChangeMaskRec	 change_mask;

  if (((XimCommon)ic->core.im)->local_impart == NULL)
    return NULL;

  XIMP_SET_NULLMASK(change_mask);
  if((ret = _Ximp_Local_SetICValueData((Ximp_XIC)ic, arg,
				       XIMP_SET_IC, &change_mask)))
    return(ret);

  Ximp_Local_Status_Set(ic);
  Ximp_Local_Status_Start(ic);
  Ximp_Local_Status_Draw(ic);
  if ((ic->ximp_icpart->value_mask & XIMP_RES_NAME)
      || (ic->ximp_icpart->value_mask & XIMP_RES_CLASS) )
    _Ximp_SetValue_Resource((Ximp_XIC)ic, &change_mask);
  return(ret);
}

static char *_Ximp_Local_MbReset(XIC xic) {
  XicCommon ic = (XicCommon)xic;
  XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;

  if (ic->local_icpart == NULL)
    return NULL;

  ic->local_icpart->composed = (DefTree *)NULL;
  if (lim->use_binary_table == True)
    ic->local_icpart->context = (DefTree *)BCF_STATE_PARSER(lim->state_addr, (CARD32)(ic->local_icpart->imstate));
  else
    ic->local_icpart->context = ic->local_icpart->imstate->parser;

  return(NULL);
}

static wchar_t *
_Ximp_Local_WcReset(XIC xic) {
  XicCommon ic = (XicCommon)xic;
  XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;

  ic->local_icpart->composed = (DefTree *)NULL;
  if (lim->use_binary_table == True)
    ic->local_icpart->context = (DefTree *)BCF_STATE_PARSER(lim->state_addr, (CARD32)(ic->local_icpart->imstate));
  else
    ic->local_icpart->context = ic->local_icpart->imstate->parser;

  return(NULL);
}

static int
_Ximp_Local_MbLookupString(XIC xic, XKeyEvent *ev, char * buffer,
			   int bytes, KeySym *keysym, Status *status) {
  XicCommon ic = (XicCommon)xic;
  int ret;
  static XComposeStatus	compose_status;
  XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;

  if (ev->type != KeyPress) {
    if (status) *status = XLookupNone;
    return (0);
  }
  if (ev->keycode == XIM_COMPOSE_KEYCODE) { /* Composed Event */
    char *mb;
    KeySym keysym_return;
    if (NULL == ic->local_icpart->composed) {
      return 0;
    }
    if (lim->use_binary_table == True){
      mb = BCF_STR_STRING(lim->str_addr, BCF_DEF_MB(lim->def_addr, (CARD32)(ic->local_icpart->composed)));
      keysym_return = BCF_DEF_KEYSYM_RETURN(lim->def_addr, (CARD32)(ic->local_icpart->composed));
    }
    else {
      mb = ic->local_icpart->composed->mb;
      keysym_return = ic->local_icpart->composed->keysym_return;
    }

    if (NULL == mb) {
      return 0;
    }

    ret = strlen(mb);
    if (ret > bytes) {
      if (status) *status = XBufferOverflow;
      return (ret);
    }
    bcopy(mb, buffer, ret);
    if (keysym) {
      if(keysym_return)
	*keysym = keysym_return;
      else
	*keysym = NoSymbol;
    }
    if (status) {
      if (keysym_return)
	*status = XLookupBoth;
      else
	*status = XLookupChars;
    }
    if (ic->local_icpart->imstate->type == CodeInputState) {
      Ximp_Local_Preedit_Done(ic);
      _Ximp_Local_SetFocus(xic);
      return(ret);
    }
    Xfree(ic->local_icpart->composed->mb);
    ic->local_icpart->composed->mb = NULL;
    XFree(ic->local_icpart->composed);
    ic->local_icpart->composed = (DefTree *)NULL;
    return (ret);
  } else { /* Throughed Event */
    (void)memset((void *)(&compose_status), 0, sizeof(XComposeStatus));
    ret = _Ximp_LookupMBText((Ximp_XIC)ic, ev, (unsigned char *)buffer,
			     bytes, keysym, &compose_status);
    if (ret > 0) {
      if (keysym && *keysym != NoSymbol) {
	if (status) *status = XLookupBoth;
      } else {
	if (status) *status = XLookupChars;
      }
    } else {
      if (keysym && *keysym != NoSymbol) {
	if (status) *status = XLookupKeySym;
      } else {
	if (status) *status = XLookupNone;
      }
    }
  }
  return (ret);
}

static int
_Ximp_Local_WcLookupString(XIC xic, XKeyEvent *ev, wchar_t * buffer, 
			   int wlen, KeySym *keysym, Status *status) {
  XicCommon ic = (XicCommon)xic;
  int ret;
  static XComposeStatus	compose_status;
  XIMComposeIM lim = ((XimCommon)ic->core.im)->local_impart;
  char *mb;
  size_t mb_len, wc_len;
  wchar_t *wc_buf;
  XimCommon im = (XimCommon)ic->core.im;

  if (ev->type != KeyPress) {
    if (status) *status = XLookupNone;
	return (0);
    }
  if (ev->keycode == XIM_COMPOSE_KEYCODE) { /* Composed Event */
    KeySym keysym_return;
    if (NULL == ic->local_icpart->composed) {
      return 0;
    }
    if (lim->use_binary_table == True){
      mb = BCF_STR_STRING(lim->str_addr, BCF_DEF_MB(lim->def_addr, (CARD32)(ic->local_icpart->composed)));
    } else {
      mb = ic->local_icpart->composed->mb;
    }
    mb_len = strlen(mb);
    wc_buf = (wchar_t *)Xmalloc(sizeof(wchar_t) * (mb_len + 1));

    wc_len = IIimpMbstoWcs(im, mb, mb_len,
			   wc_buf, mb_len, NULL);
    if(wc_len < 0)
      wc_len = 0;
    wc_buf[wc_len] = (wchar_t)0;

    ret = _Xwcslen(wc_buf);
    if (ret > wlen) {
      if (status) *status = XBufferOverflow;
      Xfree(wc_buf);
      return (ret);
    }
    bcopy(wc_buf, buffer, ret * sizeof(wchar_t));
    Xfree(wc_buf);

    if (lim->use_binary_table == True) {
      keysym_return = BCF_DEF_KEYSYM_RETURN(lim->def_addr, (CARD32)(ic->local_icpart->composed));
    } else {
      keysym_return = ic->local_icpart->composed->keysym_return;
    }

    if (keysym) {
      if (keysym_return)
	*keysym = keysym_return;
      else
	*keysym = NoSymbol;
    }
    if (status) {
      if (keysym_return)
	*status = XLookupBoth;
      else
	*status = XLookupChars;
    }
    if (ic->local_icpart->imstate->type == CodeInputState) {
      Ximp_Local_Preedit_Done(ic);
      _Ximp_Local_SetFocus(xic);
      return(ret);
    }
    if (ic->local_icpart->composed->mb != NULL) {
      Xfree(ic->local_icpart->composed->mb);
      ic->local_icpart->composed->mb = NULL;
    }
    XFree(ic->local_icpart->composed);
    ic->local_icpart->composed = (DefTree *)NULL;
    return (ret);
  } else { /* Throughed Event */
    (void)memset((void *)(&compose_status), 0, sizeof(XComposeStatus));
    ret = _Ximp_LookupWCText((Ximp_XIC)ic, ev, buffer, wlen, keysym, &compose_status);
    if(ret > 0) {
      if(keysym && *keysym != NoSymbol) {
	if(status) *status = XLookupBoth;
      } else {
	if(status) *status = XLookupChars;
      }
    } else {
      if(keysym && *keysym != NoSymbol) {
	if(status) *status = XLookupKeySym;
      } else {
	if(status) *status = XLookupNone;
      }
    }
  }
  return (ret);
}

/* Switching */
static Status
SWITCH_CloseIM(xim)
XIM xim;
{
  XimCommon im = (XimCommon)xim;
  
  if (!im->local_impart) return True;

  if (im->local_impart->use_binary_table == True){
    if(im->local_impart->map_addr)
      munmap(im->local_impart->map_addr, im->local_impart->map_len);
  }
  else{
    LocalIMState  *state = im->local_impart->top_state;
    LocalIMState  *state_p;

    while (state) {
      if(state->name) XFree(state->name);
      state->name = (char *)NULL;

      if(state->language)
	XFree(state->language);
      state->language = (char *)NULL;

      if (state->parser)
	FreeDefTreeElements(state->parser);
      state->parser = (DefTree *)NULL;
      if (state->attr) {
	if (state->type == LookupState) {
	  if (state->attr->_attr_u.LUstruct.title != NULL)
	    Xfree(state->attr->_attr_u.LUstruct.title);
	  state->attr->_attr_u.LUstruct.title = NULL;
			
	  if (state->attr->_attr_u.LUstruct.range != NULL)
	    Xfree(state->attr->_attr_u.LUstruct.range);
	  state->attr->_attr_u.LUstruct.range = NULL;
	}
	Xfree(state->attr);
      }
      state->attr = NULL;
      state_p = state;
      state = state->next;
      Xfree(state_p);
      state_p = NULL;
    }
  }
  if (im->ximp_impart->im_styles) {
    XFree(im->ximp_impart->im_styles->supported_styles);
    XFree(im->ximp_impart->im_styles);
  }
  XFree(im->local_impart);
  im->local_impart = 0;
  return True;
}

static void
SWITCH_DestroyIC(XIC xic) {
  XicCommon ic = (XicCommon)xic;
  XimCommon im = (XimCommon)ic->core.im;
  LocalStatusExt *hook;

  if (XIM_IS_SWITCH(im)) {
    UnRegisterSwitchFilter(ic, SwitchFilter,
			   Ximp_Local_KeyFilter,
			   get_local_ic_methods());
  }

  if (((XimCommon)ic->core.im)->local_impart->current_ic == (XIC)ic) {
    _Ximp_Local_UnSetFocus((XIC)ic);
  }

  if (ic->local_icpart) {
    hook = (LocalStatusExt*)(ic->local_icpart->status_ext);
    if (hook) {
      if (hook->gc) XFreeGC(ic->core.im->core.display, hook->gc);
      if (hook->rgc) XFreeGC(ic->core.im->core.display, hook->rgc);
      Xfree(hook);
    }
    Ximp_Local_Table_Destroy(ic); /* destroy table_ext */
    Ximp_Local_Preedit_Destroy(ic); /* destroy preedit_ext */
    Ximp_Local_Lookup_Destroy(ic); /* destroy lookup_ext */

    if (ic->gui_icpart) {
      XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_DESTROY, NULL);
      XIC_GUI(ic, change_status)((XIC)ic, STATUS_DESTROY, NULL);
      XIC_GUI(ic, change_lookup)((XIC)ic, LOOKUP_DESTROY, NULL);
    }

    if (ic->local_icpart->composed) {
      if (ic->local_icpart->composed->mb) {
	Xfree(ic->local_icpart->composed->mb);
	ic->local_icpart->composed->mb = NULL;
      }
      Xfree(ic->local_icpart->composed);
      ic->local_icpart->composed = NULL;
    }
  }
  if (ic->ximp_icpart->status_font)
    Xfree(ic->ximp_icpart->status_font);
  if (ic->ximp_icpart->preedit_font)
    Xfree(ic->ximp_icpart->preedit_font);

  if (ic->local_icpart) {
    Xfree(ic->local_icpart);
    ic->local_icpart = 0;
  }

  _XUnregisterFilter(ic->core.im->core.display, ic->core.focus_window,
		     Ximp_Local_KeyFilter, (XPointer)ic);
  return;
}

/*
 * Resource Extension function is called from XOpenIM().
 */

#ifndef SINGLE_XI18NLIB
void
_Ximp_OpenIMResourceExtension(im)
Ximp_XIM im;
{
  /* Add extension here */
  return;
}
#endif

static void
ComposeKeyEventFilter(Ximp_XIC ic, Window old_focus) {
  _XUnregisterFilter(ic->core.im->core.display, old_focus,
		     Ximp_Local_KeyFilter, (XPointer)ic);
  _XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			 KeyPress, KeyRelease,
			 Ximp_Local_KeyFilter, (XPointer)ic);
  return;
}
