/*
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.

*/
#include "xiiimp.h"
#include "iiimpIM.h"
#include "iiimpColor.h"
#include "guiIMPre.h"
#include "XimpIm.h"
#include "xfactory.h"
#include "trace_message.h"

static void UpdatePreedit(XicCommon, int, int);

static void
DrawPreeditString(XicCommon ic,
		  Display *display, Window win, XFontSet fontset,
		  GC gc, GC rgc, int x, int y, XIMFeedback *feedback,
		  IMFeedbackList *feedback_list,
		  wchar_t *wstr, int offset, int len) {
  if (fontset == NULL) return;

  wstr += offset;
  if (feedback != NULL) {
    XIMFeedback *start = (XIMFeedback*)feedback + offset;
    XIMFeedback *current;
    IMFeedbackList *start_flist = 0;
    IMFeedbackList *current_flist;
    int left = len;
    int nchars;
    wchar_t *wcstr = wstr;
    int under_line = y + 1;
    int pos_x = x;
    int width;

    if (feedback_list) {
      start_flist = &feedback_list[offset];
    }
    while (0 < left) {
      current = start;
      current_flist = start_flist;
      nchars = 0;

      while (*current == *start &&
	     SameIMFeedbackList(current_flist, start_flist)) {
	nchars++;
	left--;
	if (left == 0) break;
	current++;
	current_flist++;
      }
      /* set colors */
      SetIMColors(ic, display, win, gc, rgc, start_flist, *start);

      if (*start & XIMReverse) {
	XwcDrawImageString(display, win, fontset, rgc, 
			   pos_x, y, wcstr, nchars);
      } else {
	XwcDrawImageString(display, win, fontset, gc, 
			   pos_x, y, wcstr, nchars);
      }
      width = XwcTextEscapement(fontset, wcstr, nchars);
      if (*start & XIMUnderline) {
	if (*start & XIMReverse)
	  XDrawLine(display, win, rgc, 
		    pos_x, under_line, pos_x + width, under_line);
	else
	  XDrawLine(display, win, gc, 
		    pos_x, under_line, pos_x + width, under_line);
      }
      wcstr += nchars;
      pos_x += width;
      start = current;
      start_flist = current_flist;
    }
  } else {
    XwcDrawString(display, win, fontset, gc, x, y, wstr, len);
  }
}

Bool
FilterConfigureNotify(Display *d, Window w, XEvent *ev, XPointer client_data) {
  XicCommon ic = (XicCommon)client_data;

  if (!ic->gui_icpart || NULL == XIC_GUI(ic, preedit)) {
    /* perhaps the ic has been destroyed already */
    return False;
  }
  if (NULL != ev) {
    if (ConfigureNotify == ev->type) {
      if (((XConfigureEvent *)ev)->window == ic->core.focus_window) {
	XIC_GUI(ic, preedit)->client_window_width =
		((XConfigureEvent *)ev)->width;
	XIC_GUI(ic, preedit)->client_window_height =
		((XConfigureEvent *)ev)->height;
	XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_WIN, NULL);
	UpdatePreedit(ic, 0, 0);
      }
    }
  } else {
    if (w == ic->core.focus_window) {
      XWindowAttributes attr;
      if (XGetWindowAttributes(d, w, &attr)) {
	XIC_GUI(ic, preedit)->client_window_width = attr.width;
	XIC_GUI(ic, preedit)->client_window_height = attr.height;
      }
    }
  }

  return False;
}

static Bool
FilterKeyPress(Display *d, Window w, XEvent *ev, XPointer client_data) {
  XicCommon ic = (XicCommon)client_data;
  IMForwardEvent(ic, ev);
  PutBackXKeyEvent(ic);
  return True;
}

static Bool
RepaintPreedit(Display *d, Window w, XEvent *ev, XPointer client_data) {
  XicCommon ic = (XicCommon)client_data;
  PreeditWin preedit = NULL;

  if (!ic->gui_icpart) return True;
  preedit = (PreeditWin)ic->gui_icpart->preedit;
  
  if (!preedit) return True;

  if (True == XIC_GUI(ic, preedit)->discard_expose_event) {
    /*
     * when preedit window is created or reconfigured, expose
     * event will be delivered.  xiiimp.so always updates
     * contents of the window before receiving expose event,
     * there is no need to update the contents here.
     */
    XIC_GUI(ic, preedit)->discard_expose_event = False;
    return True;
  }

  /* multiple expose event */
  if (0 != ((XExposeEvent *)ev)->count) return True;

  UpdatePreedit(ic, 0, 0);
  return True;
}

static void
UpdatePreeditAll(XicCommon ic) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditArea preedit_area = preedit->preedit_areas;
  PreeditChars preedit_chars = (PreeditChars)&(preedit->preedit_chars);
  int i;

  if (preedit_chars->wchar == 0) return;

  /* Draw All Preedit Text */
  for (i = 0; i < preedit->active_areas; i++) {
    if (ic->core.input_style & XIMPreeditArea) {
      XClearArea(ic->core.im->core.display,
		 preedit_area[i].window, 0, 0, 0, 0, False);
    }
    if (preedit_area[i].active_lines == 0) {
      DrawPreeditString(ic,
			ic->core.im->core.display,
			preedit_area[i].window,
			preedit->fontset,
			preedit->gc, preedit->rgc,
			preedit_area[i].x,
			preedit_area[i].y,
			preedit_chars->feedback,
			preedit_chars->feedback_list,
			preedit_chars->wchar,
			preedit_area[i].char_offset,
			preedit_area[i].char_len);
    } else {
      int j;
      PreeditLine line = (PreeditLine)preedit_area[i].lines;
      for (j = 0; j < preedit_area[i].active_lines; j++) {
	DrawPreeditString(ic,
			  ic->core.im->core.display,
			  preedit_area[i].window,
			  preedit->fontset,
			  preedit->gc, preedit->rgc,
			  line[j].x,
			  line[j].y,
			  preedit_chars->feedback,
			  preedit_chars->feedback_list,
			  preedit_chars->wchar,
			  line[j].char_offset,
			  line[j].char_len);
      }
    }
  }
}

static void
preedit_window_fg_and_bg(XicCommon ic, unsigned long *fg,
			 unsigned long *bg) {
  Display *display = ic->core.im->core.display;

  if (XIMP_CHK_PREFGMASK(ic) && XIMP_CHK_PREBGMASK(ic)) {
    *fg = ic->core.preedit_attr.foreground;
    *bg = ic->core.preedit_attr.background;
  } else {
    *fg = BlackPixel(display, DefaultScreen(display));
    *bg = WhitePixel(display, DefaultScreen(display));
  }
  return;
}

static void
create_preedit_gc(Display *display, Window win,
		  PreeditWin preedit,
		  unsigned long fg, unsigned long bg) {
  unsigned long val_mask;
  XGCValues gcval;

  val_mask = GCForeground | GCBackground;
  gcval.foreground = fg;
  gcval.background = bg;
  preedit->gc = XCreateGC(display, win, val_mask, &gcval);
  gcval.foreground = bg;
  gcval.background = fg;
  preedit->rgc = XCreateGC(display, win, val_mask, &gcval);
}
			 
void
UnmapPreeditWindow(XicCommon ic, PreeditArea preedit_area) {
  if (False == preedit_area->mapped) {
    return;
  }

  XUnmapWindow(ic->core.im->core.display, preedit_area->window);
  preedit_area->mapped = False;
  preedit_area->win_config.x = -1;
  preedit_area->win_config.y = -1;
  preedit_area->win_config.height = -1;
  preedit_area->win_config.width = -1;

  return;
}

static void
UpdatePreedit(XicCommon ic, int start, int len) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditArea preedit_area = preedit->preedit_areas;
  PreeditChars preedit_chars;
  int i;

  if (!preedit) return;

  if (!preedit->preedit_areas) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    if (!preedit->preedit_areas)
      return;
  }
  preedit_area = preedit->preedit_areas;
  preedit_chars = (PreeditChars)&(preedit->preedit_chars);

  if (preedit_chars->wchar_len == 0) {
    for (i = 0; i < preedit->active_areas; i++) {
      UnmapPreeditWindow(ic, preedit_area + i);
    }
    return;
  }
  if (preedit->gc == 0 || preedit->rgc == 0) {
    unsigned long fg, bg;
    preedit_window_fg_and_bg(ic, &fg, &bg);
    create_preedit_gc(ic->core.im->core.display,
		      preedit_area[0].window, preedit,
		      fg, bg);
  }

  if (!preedit->fontset) {
    SetPreeditFont(ic, NULL);
  }
  for (i = 0; i < preedit->active_areas; i++) {
    if (preedit_area[i].char_len == 0) {
      UnmapPreeditWindow(ic, preedit_area + i);
      continue;
    }
    if (False == preedit_area[i].mapped) {
      XMapWindow(ic->core.im->core.display, preedit_area[i].window);
      preedit_area[i].mapped = True;
    }
  }
  for (i = preedit->active_areas; i < preedit->alloc_areas; i++) {
    UnmapPreeditWindow(ic, preedit_area + i);
  }
  if (preedit_chars->wchar_len == 0) {
    for (i = 0; i < preedit->active_areas; i++) {
      XClearArea(ic->core.im->core.display,
		 preedit_area[i].window, 0, 0, 0, 0, False);
    }
  }
  if (start == 0 && len == 0) {
    UpdatePreeditAll(ic);
  } else if (ic->core.input_style & XIMPreeditArea &&
	     (preedit_area[0].configured == True ||
	      preedit_area[0].char_len !=
	      preedit_area[0].char_len_backup)) {
    UpdatePreeditAll(ic);
  } else if (len > 0) {
    int x;
    unsigned int width;
    wchar_t *wcstr = preedit_chars->wchar;

    for (i = preedit->active_areas - 1; i >= 0; i--) {
      if (preedit_area[i].active_lines == 0) {
	int char_offset = preedit_area[i].char_offset;
	int char_len = preedit_area[i].char_len;
	int start_clear;
	int end_clear;

	if (True == preedit_area[i].configured) {
	  start_clear = char_offset;
	  end_clear = start_clear + char_len;
	  width = XwcTextEscapement(preedit->fontset,
				    wcstr + start_clear, char_len);
	  x = 0;
	  preedit_area[i].configured = False;

	} else {
	  if (char_offset + char_len < start) continue;
	  if (char_offset > start + len) continue;

	  start_clear = start > char_offset ? start : char_offset;
	  if ((True == XIC_GUI(ic, preedit)->discard_expose_event) &&
	      ((start + len) <= (char_offset + char_len))) {
	    end_clear = (char_offset + char_len);
	  } else {
	    end_clear = (((start + len) < (char_offset + char_len)) ?
			 (start + len) : (char_offset + char_len));
	  }

	  if ((start + len) < (char_offset + char_len)) {
	    width = XwcTextEscapement(preedit->fontset, wcstr + start_clear,
				      end_clear - start_clear);
	  } else {
	    width = 0;
	  }
	  if (0 < (start_clear - char_offset)) {
	    x = XwcTextEscapement(preedit->fontset, wcstr + char_offset,
				  start_clear - char_offset);
	  } else {
	    x = 0;
	  }
	}

	TRACE_MESSAGE('p', ("UpdatePreedit: "
			    "len=%d start=%d end=%d "
			    "char_offset=%d char_len=%d x=%d width=%d "
			    "area[%d].x=%d, area[%d].y=%d "
			    "\n",
			    len, start_clear, end_clear,
			    char_offset, char_len, x, width,
			    i, preedit_area[i].x, i, preedit_area[i].y));
	if ((preedit_chars->wchar[0]) && (0 < (end_clear - start_clear))) {
	  XClearArea(ic->core.im->core.display,
		     preedit_area[i].window,
		     preedit_area[i].x + x, preedit_area[i].y,
		     width, 0, False);
	  DrawPreeditString(ic,
			    ic->core.im->core.display,
			    preedit_area[i].window,
			    preedit->fontset,
			    preedit->gc, preedit->rgc,
			    preedit_area[i].x + x, preedit_area[i].y,
			    preedit_chars->feedback,
			    preedit_chars->feedback_list,
			    preedit_chars->wchar,
			    start_clear, end_clear - start_clear);
	}
      } else {
	PreeditLine line = (PreeditLine)preedit_area[i].lines;
	int j;
	XClearArea(ic->core.im->core.display,
		   preedit_area[i].window, 0, 0, 0, 0, False);
	for (j = 0; j < preedit_area[i].active_lines; j++) {	
	  DrawPreeditString(ic,
			    ic->core.im->core.display,
			    preedit_area[i].window,
			    preedit->fontset,
			    preedit->gc, preedit->rgc,
			    line[j].x,
			    line[j].y,
			    preedit_chars->feedback,
			    preedit_chars->feedback_list,
			    preedit_chars->wchar,
			    line[j].char_offset,
			    line[j].char_len);
	}
      }
    }
  }
}

/* Public Methods */
Bool
SetupPreeditExt(XicCommon ic) {
  PreeditWin preedit = (PreeditWin)Xmalloc(sizeof(PreeditWinRec));

  if (!preedit) return False;
  memset((char *)preedit, 0, sizeof(PreeditWinRec));

  preedit->alloc_areas = 0;
  preedit->active_areas = 0;
  preedit->need_free_fontset = False;

  ic->gui_icpart->preedit = (void *)preedit;
  return True;
}

Bool
NewPreeditWindow(XicCommon ic) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  Window win;
  int x, y;
  unsigned int width, height;
  unsigned long fg, bg;
  Display *display = ic->core.im->core.display;
  PreeditArea preedit_area = (PreeditArea)NULL;
  int n;
  int nn;
  int mask;
  XIMFilterRec filters[2];
  XClassHint class_hint;

  if (!preedit) return False;

  if (XIMP_CHK_PREAREAMASK(ic)) {
    x = ic->core.preedit_attr.area.x;
    y = ic->core.preedit_attr.area.y;
    width   = ic->core.preedit_attr.area.width;
    height  = ic->core.preedit_attr.area.height;
  } else if (XIMP_CHK_PRESPOTLMASK(ic)) {
    x = ic->core.preedit_attr.spot_location.x;
    y = ic->core.preedit_attr.spot_location.y;
    width = height = 1;
  } else {
    x  = y = 0;
    width = height = 1;
  }

  if (preedit->alloc_areas == 0 || !preedit->preedit_areas) {
    preedit->alloc_areas = 1;
    preedit->active_areas = 1;
    preedit->preedit_areas = (PreeditArea)Xmalloc(sizeof(PreeditAreaRec));
    if (!preedit->preedit_areas) {
      return False;
    }
    memset((char *)preedit->preedit_areas, 0, sizeof(PreeditAreaRec));
  } else {
    preedit->alloc_areas++;
    preedit->active_areas++;
    preedit->preedit_areas = (PreeditArea)
      Xrealloc(preedit->preedit_areas, sizeof(PreeditAreaRec) *
	       preedit->alloc_areas);
    if (!preedit->preedit_areas) {
      return False;
    }
  }
  preedit_area = preedit->preedit_areas;
  n = preedit->alloc_areas;

  preedit_window_fg_and_bg(ic, &fg, &bg);

  mask = None;
  nn = 0;
  filters[nn].type = KeyPress;
  filters[nn].filter = FilterKeyPress;
  filters[nn].client_data = (XPointer)ic;
  mask |= KeyPressMask;
  nn++;
  filters[nn].type = Expose;
  filters[nn].filter = RepaintPreedit;
  filters[nn].client_data = (XPointer)ic;
  nn++;
  mask |= ExposureMask;

  width = height = 1;
  win = XFactoryCreateIMWindow(display, preedit->parent,
			       ic->core.client_window,
			       x, y, width, height, bg,
			       mask, filters, nn);
  if (!win) return False;

  if (ic->core.input_style & XIMPreeditArea ||
      ic->core.input_style & XIMPreeditPosition) {
    /* set override-redirect to true */
    XSetWindowAttributes attributes;
    int cmask = 0;
    cmask |= CWOverrideRedirect;
    attributes.override_redirect = True;
    XChangeWindowAttributes(ic->core.im->core.display, win,
			    cmask, &attributes);
  }

  XStoreName(display, win, "iiimx IM Preedit");

  class_hint.res_name = "iiimx-im-preedit";
  class_hint.res_class = "IiimxImPreedit";
  XSetClassHint(display, win, &class_hint);

  if (!(ic->core.input_style & XIMPreeditNothing)) {
    /* bug fix:
          4295973: preedit text is invisible in XIMPreeditNothing
       We don't know why backing store with XIMPreeditNothing causes
       4295973, but having the codes commented out will fix it.
    */
    XSetWindowAttributes attributes;
    mask = 0;
    attributes.bit_gravity = NorthWestGravity;
    mask |= CWBitGravity;
    attributes.backing_store = WhenMapped;
    mask |= CWBackingStore;
    XChangeWindowAttributes(display, win, mask, &attributes);
  }
#if 0
  if (XIMP_CHK_PREFGMASK(ic) && XIMP_CHK_PREBGMASK(ic)) {
    create_preedit_gc(display, win, preedit,
		      ic->core.preedit_attr.foreground,
		      ic->core.preedit_attr.background);
  }
#endif

  preedit_area[n-1].window = win;
  preedit_area[n-1].char_offset = 0;
  preedit_area[n-1].char_len = 0;
  preedit_area[n-1].char_offset_backup = 0;
  preedit_area[n-1].char_len_backup = 0;
  preedit_area[n-1].active_lines = 0;
  preedit_area[n-1].alloc_lines = 0;
  preedit_area[n-1].mapped = False;
  preedit_area[n-1].win_config.x = x;
  preedit_area[n-1].win_config.y = y;
  preedit_area[n-1].win_config.width = width;
  preedit_area[n-1].win_config.height = height;


  return True;
}

Bool
SetupPreeditWindow(XicCommon ic, Window parent) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  int x, y;
  Display *display = ic->core.im->core.display;
  PreeditArea preedit_area = (PreeditArea)NULL;
  XWindowAttributes cwin_att;
  int i;

  if (!preedit) return False;

  /* A new parent window is the same as the previous, nothing to do. */
  if (preedit->preedit_areas) {
    if (parent == 0 || preedit->parent == parent)
      return False;

    /* on Linux, sometimes parent window is invalid */
    if (preedit->parent && !IMCheckIMWindow(ic, preedit->parent)) {
      preedit->alloc_areas=0;
    }
  }

  preedit->parent = parent;

  if (XIMP_CHK_PREAREAMASK(ic)) {
    x = ic->core.preedit_attr.area.x;
    y = ic->core.preedit_attr.area.y;
  } else if (XIMP_CHK_PRESPOTLMASK(ic)) {
    x = ic->core.preedit_attr.spot_location.x;
    y = ic->core.preedit_attr.spot_location.y;
  } else {
    x  = y = 0;
  }

  if (preedit->alloc_areas > 0) {
    preedit_area = preedit->preedit_areas;
    for (i = 0; i < preedit->alloc_areas; i++) {
      if (preedit_area[i].window) {
	/* Make sure the window gets unmapped */
        preedit_area[i].mapped = True;
	UnmapPreeditWindow(ic, preedit_area + i);
	XReparentWindow(display, preedit_area[i].window,
			preedit->parent,
			x, y);
      }
    }
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_MOVE, NULL);
    /* redraw if any text in preedit windows */
    UpdatePreedit(ic, 0, 0);
    return True;
  } else {
    if (XGetWindowAttributes(ic->core.im->core.display, 
              ic->core.client_window, &cwin_att) && 
              (cwin_att.map_state == IsViewable))
      return NewPreeditWindow(ic);
    else 
      return False;
  }
}

void
SetPreeditForeground(XicCommon ic, XPointer call_data) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  CacheRec *preedit_cache;

  if (!preedit) return;  /* Let's do it later */

  preedit_cache = (CacheRec*)&(preedit->preedit_cache);

  if (preedit_cache->foreground == ic->core.preedit_attr.foreground) {
    return;
  }
#if 0
  if (preedit->gc) {
    XGCValues	val;
    unsigned long mask;
    val.foreground = ic->core.preedit_attr.foreground;
    mask = GCForeground;
    XChangeGC(ic->core.im->core.display,
	      preedit->gc,
	      mask,
	      &val);
  }

  if (preedit->rgc) {
    XGCValues	val;
    unsigned long mask;
    val.background = ic->core.preedit_attr.foreground;
    mask = GCBackground;
    XChangeGC(ic->core.im->core.display,
	      preedit->rgc,
	      mask,
	      &val);
  }
#endif
  preedit_cache->foreground = ic->core.preedit_attr.foreground;
  return;
}

void
SetPreeditBackground(XicCommon ic, XPointer call_data) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  CacheRec *preedit_cache;
  int i;

  if (!preedit) return;  /* Let's do it later */

  preedit_cache = (CacheRec*)&(preedit->preedit_cache);

  TRACE_MESSAGE('p', ("SetPreeditBackground: %d %d\n",
		      preedit_cache->background,
		      ic->core.preedit_attr.background));

  if (preedit_cache->background == ic->core.preedit_attr.background) {
    return;
  }

  for (i = 0; i < preedit->alloc_areas; i++) {
    if (preedit->preedit_areas[i].window) {
      XSetWindowBackground(ic->core.im->core.display,
			   preedit->preedit_areas[i].window,
			   ic->core.preedit_attr.background);
    }
  }
#if 0
  if (preedit->gc) {
    XGCValues	val;
    unsigned long mask;
    val.background = ic->core.preedit_attr.background;
    mask = GCBackground;
    XChangeGC(ic->core.im->core.display,
	      preedit->gc,
	      mask,
	      &val);
  }
  if (preedit->rgc) {
    XGCValues	val;
    unsigned long mask;
    val.foreground = ic->core.preedit_attr.background;
    mask = GCForeground;
    XChangeGC(ic->core.im->core.display,
	      preedit->rgc,
	      mask,
	      &val);
  }
#endif
  preedit_cache->background = ic->core.preedit_attr.background;
  return;
}

static void
UpdatePreeditWidth(XicCommon ic) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditChars preedit_chars;
  int i;
  int text_length;
  wchar_t *wc_p;

  preedit_chars = &(preedit->preedit_chars);
  text_length = preedit_chars->wchar_len;

  for (wc_p = preedit_chars->wchar, i = 0; i < text_length; i++) {
    *(preedit_chars->wchar_width + i) =
      XwcTextEscapement(preedit->fontset, wc_p + i, 1);
  }
  UpdatePreedit(ic, 0, text_length);
}

void
SetPreeditFont(XicCommon ic, XPointer call_data) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);

  if (!preedit) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    preedit = (PreeditWin)ic->gui_icpart->preedit;
    if (!preedit) return;
  }
  if (ic->core.preedit_attr.fontset) {
    if (preedit->fontset && preedit->need_free_fontset) {
      XFactoryFreeDefaultFontSet (ic->core.im->core.display);
      preedit->need_free_fontset = False;
    }
    preedit->fontset = ic->core.preedit_attr.fontset;
  } else if (preedit->fontset == NULL) {
    preedit->fontset = XFactoryCreateDefaultFontSet(ic->core.im->core.display,
						    XIM_IIIMP(ic->core.im, default_font_name));
    preedit->need_free_fontset = True;
  }
  preedit->fse = XExtentsOfFontSet(preedit->fontset);

  XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_WIN, NULL);
  UpdatePreeditWidth(ic);
  XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_MOVE, NULL);
  return;
}

static void
PreeditReplaceString(XicCommon ic, PreeditWin preedit,
		     int *change_first, int *change_length,
		     XIMText *text, IMFeedbackList *feedback_list) {
  int	first_real;
  int	length_real;
  int	first_orig;
  int	length_orig;
  int	i = 0;
  int	j;
  int	m;
  int	text_length;
  PreeditChars preedit_chars;
  PreeditChars preedit_chars_backup;
  wchar_t *cur_char;
  wchar_t *chg_char;
  wchar_t wc_buf[1024];
  wchar_t *wc_p;
  wchar_t *wc_p_free;
  IMFeedbackList *src_fbl;
  IMFeedbackList *dst_fbl;
  IMFeedbackList *cur_fbl;
  IMFeedbackList *chg_fbl;
  XIMFeedback *cur_xfb;
  XIMFeedback *chg_xfb;
  IMFeedback  *cur_fb;
  IMFeedback  *chg_fb;

  preedit_chars = &(preedit->preedit_chars);

  if (preedit_chars->wchar_len < *change_first) {
    first_orig  = preedit_chars->wchar_len;
    length_orig = 0;
  } else if (preedit_chars->wchar_len < (*change_first + *change_length)) {
    first_orig  = *change_first;
    length_orig = (preedit_chars->wchar_len - *change_first);
  } else {
    first_orig  = *change_first;
    length_orig = *change_length;
  }
  first_real = first_orig;
  length_real = length_orig;

  wc_p_free = NULL;

  /*
   * dtermine the real change
   */
  if (NULL == text) {
    wc_p = NULL;
    text_length = 0;
    *change_first = first_real;
    *change_length = length_real;
  } else {
    text_length = text->length;
    if (text->encoding_is_wchar) {
      wc_p = text->string.wide_char;
    } else {
      wc_p = wc_buf;
      if (((sizeof (wc_buf)) / (sizeof (wchar_t))) < text_length) {
	wc_p = Xmalloc((sizeof (wchar_t)) * text_length);
	if (NULL == wc_p) return;
	wc_p_free = wc_p;
      }
      IIimpMbstoWcs((XimCommon)ic->core.im,
		    text->string.multi_byte, strlen(text->string.multi_byte),
		    wc_p, text_length, NULL);
    }

    cur_char = (preedit_chars->wchar + first_real);
    cur_fbl = (preedit_chars->feedback_list + first_real);
    cur_xfb = (preedit_chars->feedback + first_real);
    chg_char = wc_p;
    chg_fbl = feedback_list;
    chg_xfb = text->feedback;

    /*
     * calculate first_real
     */
    for (i = 0; i < length_real; i++) {
      if (*(cur_char + i) != *(chg_char + i)) {
	break;
      }

      cur_fb = cur_fbl[i].feedbacks;
      if (NULL == chg_fbl) {
	for (j = 0; j < cur_fbl[i].count_feedbacks; j++) {
	  if ((IM_DECORATION_FEEDBACK != (cur_fb + j)->type) ||
	      (0 != (cur_fb + j)->value)) {
	    break;
	  }
	}
	if (j != cur_fbl[i].count_feedbacks) {
	  break;
	}
      } else {
	if (cur_fbl[i].count_feedbacks != chg_fbl[i].count_feedbacks) {
	  break;
	}
	chg_fb = chg_fbl[i].feedbacks;
	for (j = 0; j < cur_fbl[i].count_feedbacks; j++) {
	  if (((cur_fb + j)->type  != (chg_fb + j)->type) ||
	      ((cur_fb + j)->value != (chg_fb + j)->value)) {
	    break;
	  }
	}
	if (j != cur_fbl[i].count_feedbacks) {
	  break;
	}
      }

      if (NULL == chg_xfb) {
	if (*(cur_xfb + i) != 0) {
	  break;
	}
      } else {
	if (*(cur_xfb + i) != *(chg_xfb + i)) {
	  break;
	}
      }
    }
    first_real += i;

    cur_char = (preedit_chars->wchar + *change_first + *change_length - 1);
    cur_fbl = ((NULL == preedit_chars->feedback_list) ? NULL :
	       (preedit_chars->feedback_list +
		*change_first + *change_length - 1));
    cur_xfb = ((NULL == preedit_chars->feedback) ? NULL :
	       (preedit_chars->feedback + *change_first + *change_length - 1));
    chg_char = (wc_p + text_length - 1);
    chg_fbl = ((NULL == feedback_list) ? NULL :
	       (feedback_list + text_length - 1));
    chg_xfb = ((NULL == text->feedback) ? NULL :
	       (text->feedback + text_length - 1));

    /*
     * calculate length_real
     */
    if (0 == *change_length) {
      m = 0;
    } else {
      m = ((text_length < *change_length) ? *change_length : text_length);
    }
    for (i = 0; i < m; i++) {
      if (*(cur_char - i) != *(chg_char - i)) {
	break;
      }

      cur_fb = (cur_fbl->feedbacks - i);
      if (NULL == chg_fbl) {
	for (j = 0; j < cur_fbl->count_feedbacks; j++) {
	  if ((IM_DECORATION_FEEDBACK != (cur_fb + j)->type) ||
	      (0 != (cur_fb + j)->value)) {
	    break;
	  }
	}
	if (j != cur_fbl->count_feedbacks) {
	  break;
	}
      } else {
	if ((cur_fbl - i)->count_feedbacks != (chg_fbl - i)->count_feedbacks) {
	  break;
	}
	chg_fb = (chg_fbl->feedbacks - i);
	for (j = 0; j < cur_fbl->count_feedbacks; j++) {
	  if (((cur_fb + j)->type  != (chg_fb + j)->type) ||
	      ((cur_fb + j)->value != (chg_fb + j)->value)) {
	    break;
	  }
	}
	if (j != cur_fbl->count_feedbacks) {
	  break;
	}
      }

      if (NULL == chg_xfb) {
	if (*(cur_xfb - i) != 0) {
	  break;
	}
      } else {
	if (*(cur_xfb - i) != *(chg_xfb - i)) {
	  break;
	}
      }
    }
    length_real -= i;

    *change_first = first_real;
    if (*change_length == text_length) {
      *change_length = length_real;
    } else {
      *change_length = (preedit_chars->wchar_len
			- first_real - *change_length + text_length);
    }
  }

  /*
   * free preedit_chars_backup
   */
  preedit_chars_backup = &(preedit->preedit_chars_backup);

  Xfree((char*)preedit_chars_backup->feedback);
  Xfree((char*)preedit_chars_backup->wchar);
  Xfree((char*)preedit_chars_backup->wchar_width);
  FreeFeedbackList(preedit_chars_backup->feedback_list, 
		   preedit_chars_backup->alloc_len);

  /*
   * backup current preedit_chars
   */
  preedit_chars_backup->feedback_list = preedit_chars->feedback_list;
  preedit_chars_backup->feedback = preedit_chars->feedback;
  preedit_chars_backup->wchar = preedit_chars->wchar;
  preedit_chars_backup->wchar_width = preedit_chars->wchar_width;
  preedit_chars_backup->wchar_len = preedit_chars->wchar_len;
  preedit_chars_backup->alloc_len = preedit_chars->alloc_len;

  /*
   * allocate new preedit_chars areas
   */
  preedit_chars->wchar_len = (preedit_chars_backup->wchar_len - length_orig
			      + text_length);
  preedit_chars->alloc_len = (preedit_chars->wchar_len + 1);
  preedit_chars->wchar = Xmalloc(preedit_chars->alloc_len * (sizeof (wchar_t)));
  preedit_chars->wchar_width = Xmalloc(preedit_chars->alloc_len *
				       (sizeof (unsigned short)));
  memset(preedit_chars->wchar_width, 0,
	 preedit_chars->alloc_len * (sizeof (unsigned short)));
  preedit_chars->feedback =
	  Xmalloc(preedit_chars->alloc_len * (sizeof (XIMFeedback)));
  memset(preedit_chars->feedback, 0,
	 preedit_chars->alloc_len * (sizeof (XIMFeedback)));
  preedit_chars->feedback_list =
	  Xmalloc(preedit_chars->alloc_len * (sizeof (IMFeedbackList)));
  memset(preedit_chars->feedback_list, 0,
	 preedit_chars->alloc_len * (sizeof (IMFeedbackList)));	 

  /*
   * update wchar
   */
  if (0 != first_orig) {
    memcpy(preedit_chars->wchar, preedit_chars_backup->wchar,
	   (sizeof (wchar_t)) * first_orig);
  }
  if (0 != text_length) {
    memcpy(preedit_chars->wchar + first_orig, wc_p,
	   (sizeof (wchar_t)) * text_length);
  }
  if ((first_orig + text_length) < preedit_chars->wchar_len) {
    memcpy(preedit_chars->wchar + first_orig + text_length,
	   preedit_chars_backup->wchar + first_orig + length_orig,
	   (sizeof (wchar_t)) *
	   (preedit_chars->wchar_len - first_orig + text_length));
  }
  *(preedit_chars->wchar + preedit_chars->wchar_len) = L'\0';

  /*
   * update width
   */

  if (0 != first_orig) {
    memcpy(preedit_chars->wchar_width, preedit_chars_backup->wchar_width,
	   (sizeof (unsigned short)) * first_orig);
  }
  if (0 != text_length) {
    if (NULL == preedit->fontset) {
      SetPreeditFont(ic, NULL);
    }
    if (NULL == preedit->fontset) {
      *(preedit_chars->wchar_width + i) = 0;
    } else {
      for (i = 0; i < text_length; i++) {
	*(preedit_chars->wchar_width + first_orig + i) =
		XwcTextEscapement(preedit->fontset, wc_p + i, 1);
      }
    }
  }
  if ((first_orig + text_length) < preedit_chars->wchar_len) {
    memcpy(preedit_chars->wchar_width + first_orig + text_length,
	   preedit_chars_backup->wchar_width + first_orig + length_orig,
	   (sizeof (unsigned short)) *
	   (preedit_chars->wchar_len - first_orig + text_length));
  }

  /*
   * update feedback
   */
  if (0 != first_orig) {
    memcpy(preedit_chars->feedback, preedit_chars_backup->feedback,
	   (sizeof (XIMFeedback)) * first_orig);
  }
  if (0 != text_length) {
    memcpy(preedit_chars->feedback + first_orig, text->feedback,
	   (sizeof (XIMFeedback)) * text_length);
  } else {
    memset(preedit_chars->feedback + first_orig, 0,
	   (sizeof (XIMFeedback)) * text_length);
  }
  if ((first_orig + text_length) < preedit_chars->wchar_len) {
    memcpy(preedit_chars->feedback + first_orig + text_length,
	   preedit_chars_backup->feedback + first_orig + length_orig,
	   (sizeof (XIMFeedback)) * 
	   (preedit_chars->wchar_len - first_orig + text_length));
  }
  *(preedit_chars->feedback + preedit_chars->wchar_len) = 0;

  /*
   * update feedback_list
   */
  for (i = 0; i < first_orig; i++) {
    (preedit_chars->feedback_list + i)->count_feedbacks =
	    (preedit_chars_backup->feedback_list + i)->count_feedbacks;
    if (0 == (preedit_chars->feedback_list + i)->count_feedbacks) {
      (preedit_chars->feedback_list + i)->feedbacks = NULL;
    } else {
      (preedit_chars->feedback_list + i)->feedbacks =
	      Xmalloc((preedit_chars->feedback_list + i)->count_feedbacks *
		      (sizeof (IMFeedback)));
    }
    for (j = 0; j < (preedit_chars->feedback_list + i)->count_feedbacks; j++) {
      ((preedit_chars->feedback_list + i)->feedbacks + j)->type =
	      ((preedit_chars_backup->feedback_list + i)->feedbacks + j)->type;
      ((preedit_chars->feedback_list + i)->feedbacks + j)->value =
	      ((preedit_chars_backup->feedback_list + i)->feedbacks + j)->value;
    }
  }
  if (NULL != feedback_list) {
    for (i = first_orig; i < (first_orig + text_length); i++) {
      (preedit_chars->feedback_list + i)->count_feedbacks =
	      (feedback_list + i)->count_feedbacks;
      if (0 == (preedit_chars->feedback_list + i)->count_feedbacks) {
	(preedit_chars->feedback_list + i)->feedbacks = NULL;
      } else {
	(preedit_chars->feedback_list + i)->feedbacks =
		Xmalloc((preedit_chars->feedback_list + i)->count_feedbacks *
			(sizeof (IMFeedback)));
      }
      for (j = 0; j < (preedit_chars->feedback_list + i)->count_feedbacks; j++) {
	((preedit_chars->feedback_list + i)->feedbacks + j)->type =
		((feedback_list + i)->feedbacks + j)->type;
	((preedit_chars->feedback_list + i)->feedbacks + j)->value =
		((feedback_list + i)->feedbacks + j)->value;
      }
    }
  } else {
    for (i = first_orig; i < (first_orig + text_length); i++) {
      (preedit_chars->feedback_list + i)->count_feedbacks = 0;
      (preedit_chars->feedback_list + i)->feedbacks = NULL;
    }
  }
  dst_fbl = (preedit_chars->feedback_list + first_orig + text_length);
  src_fbl = (preedit_chars_backup->feedback_list + first_orig + text_length);
  for (i = first_orig + text_length; i < preedit_chars->wchar_len; i++) {
    dst_fbl->count_feedbacks = src_fbl->count_feedbacks;
    if (0 == dst_fbl->count_feedbacks) {
      dst_fbl->feedbacks = NULL;
    } else {
      dst_fbl->feedbacks =
	      Xmalloc(dst_fbl->count_feedbacks * (sizeof (IMFeedback)));
    }
    for (j = 0; j < dst_fbl->count_feedbacks; j++) {
      (dst_fbl->feedbacks + j)->type  = (src_fbl->feedbacks + j)->type;
      (dst_fbl->feedbacks + j)->value = (src_fbl->feedbacks + j)->value;
    }
    src_fbl += 1;
    dst_fbl += 1;
  }
  dst_fbl->count_feedbacks = 0;
  dst_fbl->feedbacks = NULL;


  Xfree(wc_p_free);

  return;
}

#if 0
static void
PreeditDelete(PreeditChars preedit_chars, int start, int len) {

  int move_len = preedit_chars->wchar_len - (start + len);
  IMFeedbackList *from, *to, *temp;
  int i;

  if (move_len < 0) return;

  if (move_len == 0) {
    from = &preedit_chars->feedback_list[start];
    for (i = 0; i < len; i++, from++) {
      if (from->feedbacks) {
	Xfree(from->feedbacks);
	from->feedbacks = 0;
      }
    }
  } else {
    to = &preedit_chars->feedback_list[start];
    from = &preedit_chars->feedback_list[start + len];
    for (i = 0; i < move_len; i++) {
      to->count_feedbacks = from->count_feedbacks;
      if (to->feedbacks) Xfree(to->feedbacks);
      to->feedbacks = from->feedbacks;
      temp = from;
      from++; to++;
      Xfree(temp);
    }
    (void)memmove((void *)(preedit_chars->feedback + start),
		  (void *)(preedit_chars->feedback + start + len),
		  sizeof(XIMFeedback) * move_len);
    (void)memmove((void *)(preedit_chars->wchar + start),
		  (void *)(preedit_chars->wchar + start + len),
		  sizeof(wchar_t) * move_len);
  }
  preedit_chars->wchar_len -= len;
  preedit_chars->wchar[preedit_chars->wchar_len] = (wchar_t)0;
  return;
}

static void
PreeditInsert(XicCommon ic, PreeditChars preedit_chars, int start,
	      XIMText *text, IMFeedbackList *feedback_list) {
  IMFeedbackList *from, *to;
  int i;

  if (!text) return;

  if (text->length == 0) return;

  if (preedit_chars->alloc_len < preedit_chars->wchar_len + text->length + 1) {
    /* reallocate */
    int prev_alloc_len=preedit_chars->alloc_len;
    preedit_chars->alloc_len += 16 + text->length + 1;
    preedit_chars->wchar = Xrealloc(preedit_chars->wchar,
				    sizeof(wchar_t) *
				    preedit_chars->alloc_len);
    preedit_chars->feedback = Xrealloc(preedit_chars->feedback,
				       sizeof(XIMFeedback) *
				       preedit_chars->alloc_len);
    preedit_chars->feedback_list = Xrealloc(preedit_chars->feedback_list,
					    sizeof(IMFeedbackList) *
					    preedit_chars->alloc_len);
    /* initialized to 0 */
    memset(preedit_chars->feedback_list+prev_alloc_len, 0,
					sizeof(IMFeedbackList) *
					(16 + text->length + 1));
  }

  if (preedit_chars->wchar_len > start) {
    /* shift the right most part first */
    (void)memmove((void *)(preedit_chars->wchar + start + text->length),
		  (void *)(preedit_chars->wchar + start),
		  sizeof(wchar_t) * (preedit_chars->wchar_len - start));
    (void)memmove((void *)(preedit_chars->feedback + start + text->length),
		  (void *)(preedit_chars->feedback + start),
		  sizeof(XIMFeedback) * (preedit_chars->wchar_len - start));
    from = &preedit_chars->feedback_list[start];
    to = &preedit_chars->feedback_list[start + text->length];
    for (i = 0; i < preedit_chars->wchar_len - start; i++, from++, to++) {
      to->count_feedbacks = from->count_feedbacks;
      to->feedbacks = from->feedbacks;
    }
  }
  /* then, copy from XIMText to preedit internal buffer */
  if (text->encoding_is_wchar) {
    (void)memmove((void *)(preedit_chars->wchar + start),
		  (void *)(text->string.wide_char),
		  sizeof(wchar_t) * text->length);
  } else {
    int len = strlen(text->string.multi_byte);
    XimCommon im = (XimCommon)ic->core.im;
    IIimpMbstoWcs(im, text->string.multi_byte, len,
		  preedit_chars->wchar + start, text->length, NULL);
  }
  if (text->feedback) {
    (void)memmove((void *)(preedit_chars->feedback + start),
		  (void *)(text->feedback),
		  sizeof(XIMFeedback) * text->length);
  } else {
    /* Let's use the previous feedback */
    XIMFeedback *tmp = preedit_chars->feedback + start;
    int i;
    for (i = 0; i < text->length; i++) {
      *tmp++ = *(preedit_chars->feedback);
    }
  }
  if (feedback_list) {
    from = feedback_list;
    to = &preedit_chars->feedback_list[start];
    for (i = 0; i < text->length; i++, from++, to++) {
      to->count_feedbacks = from->count_feedbacks;
      if (to->count_feedbacks > 0)
	to->feedbacks = Xmalloc(sizeof(IMFeedback) * to->count_feedbacks);
      if (to->feedbacks) {
	IMFeedback *p, *q;
	for (p = to->feedbacks, q = from->feedbacks;
	     p < &to->feedbacks[to->count_feedbacks];
	     p++, q++) {
	  p->type = q->type;
	  p->value = q->value;
	}
      }
    }
  } else {
    /* Let's use the previous feedback, so nothing to do */
  }
  preedit_chars->wchar_len += text->length;
  preedit_chars->wchar[preedit_chars->wchar_len] = (wchar_t)0;
  return;
}
#endif /* 0 */

static void
PreeditCursor(PreeditChars preedit_chars, int caret) {
  /* need to implement  */
  if (preedit_chars->caret_pos != caret) {
    preedit_chars->caret_pos = caret;
  }
  return;
}

void
PreeditStart(XicCommon ic, XPointer call_data) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditChars preedit_chars;

  if (!preedit) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    preedit = (PreeditWin)(ic->gui_icpart->preedit);
    if (!preedit) return;
  }
  preedit_chars = (PreeditChars)&(preedit->preedit_chars);
  preedit_chars->caret_pos = 0;
  preedit_chars->wchar_len = 0;
  preedit_chars->alloc_len = 16;
  preedit_chars->wchar = Xmalloc(sizeof(wchar_t) *
				 preedit_chars->alloc_len);
  preedit_chars->wchar_width = Xmalloc(sizeof(unsigned short) *
				       preedit_chars->alloc_len);
  preedit_chars->feedback = Xmalloc(sizeof(XIMFeedback) *
				    preedit_chars->alloc_len);
  preedit_chars->feedback_list = Xmalloc(sizeof(IMFeedbackList) *
					 preedit_chars->alloc_len);
  memset(preedit_chars->wchar, 0,
	 sizeof(wchar_t) * preedit_chars->alloc_len);
  memset(preedit_chars->wchar_width, 0,
	 sizeof(unsigned short) * preedit_chars->alloc_len);
  memset(preedit_chars->feedback, 0,
	 sizeof(XIMFeedback) * preedit_chars->alloc_len);
  memset(preedit_chars->feedback_list, 0,
	 sizeof(IMFeedbackList) * preedit_chars->alloc_len);

  FilterConfigureNotify(ic->core.im->core.display,
			ic->core.focus_window,
			NULL, (XPointer)ic);
  _XRegisterFilterByType(ic->core.im->core.display, ic->core.focus_window,
			 ConfigureNotify, ConfigureNotify,
			 FilterConfigureNotify, (XPointer)ic);

  return;
}

void
PreeditDrawText(XicCommon ic, XPointer p) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditChars preedit_chars;
  int chg_first;
  int chg_length;
  XIMDrawTextStruct *preedit_draw = (XIMDrawTextStruct*)p;
  XIMPreeditDrawCallbackStruct *call_data =
    (XIMPreeditDrawCallbackStruct*)preedit_draw->call_data;
  XIMText *text = (XIMText*)call_data->text;
  IMFeedbackList *feedback_list = preedit_draw->feedback_list;

  if (!preedit) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    preedit = (PreeditWin)(ic->gui_icpart->preedit);
    if (!preedit) return;
  }

  preedit_chars = (PreeditChars)&(preedit->preedit_chars);
  if (preedit_chars->alloc_len == 0) {
    if (text) {
      /* Start drawing preedit without calling preedit_start */
      PreeditStart(ic, NULL);
    } else {
      return;			/* nothing to do */
    }
  }

#if 0
  if (call_data->chg_first == preedit_chars->wchar_len) {
    /* append from right most position */
    if (text && text->length > 0) change_len = text->length;
  }else if ((call_data->chg_first + call_data->chg_length
	     == preedit_chars->wchar_len) && !text) {
    /* delete from right most position */
    change_len = call_data->chg_length;
  } else {
    /* other operations */
    change_len = preedit_chars->wchar_len - call_data->chg_first;
    change_len -= call_data->chg_length;
    if (text && text->length > 0) change_len += text->length;
  }
#endif /* 0 */

  chg_first = call_data->chg_first;
  chg_length = call_data->chg_length;

  TRACE_MESSAGE('p', ("PreeditDrawText: chg_first = %d chg_length = %d, %d %d, caret = %d\n",
		      call_data->chg_first, call_data->chg_length,
		      chg_first, chg_length, call_data->caret));

#if 1
  PreeditReplaceString(ic, preedit,
		       &chg_first, &chg_length, text, feedback_list);

#else /* 0 */
  if (call_data->chg_length) {
    PreeditDelete(preedit_chars,
		  call_data->chg_first, call_data->chg_length);
  }
  PreeditInsert(ic, preedit_chars, call_data->chg_first,
		text, feedback_list);
#endif /* 0 */
  PreeditCursor(preedit_chars, call_data->caret);
  XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_WIN, NULL);
#if 1
  UpdatePreedit(ic, chg_first, chg_length);
#else /* 0 */
  UpdatePreedit(ic, call_data->chg_first, change_len);
#endif /* 0 */

  return;
}

void
PreeditCaret(XicCommon ic, XPointer call_data) {
  /* do nothing */
  return;
}

void
PreeditDone(XicCommon ic, XPointer call_data) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditArea preedit_area;
  PreeditChars preedit_chars;
  int i;

  if (!preedit) return;

  _XUnregisterFilter(ic->core.im->core.display, ic->core.focus_window,
		     FilterConfigureNotify, (XPointer)ic);

  preedit_area = (PreeditArea)(preedit->preedit_areas);
  preedit_chars = (PreeditChars)&(preedit->preedit_chars);

  if (preedit_chars->feedback) Xfree((char*)preedit_chars->feedback);
  if (preedit_chars->wchar) Xfree((char*)preedit_chars->wchar);
  if (preedit_chars->wchar_width)
    Xfree((char*)preedit_chars->wchar_width);
  FreeFeedbackList(preedit_chars->feedback_list, 
		   preedit_chars->alloc_len);
  preedit_chars->feedback_list = NULL;
  preedit_chars->feedback = NULL;
  preedit_chars->wchar = NULL;
  preedit_chars->wchar_width = NULL;
  preedit_chars->wchar_len = 0;
  preedit_chars->alloc_len = 0;

  for (i = 0; i < preedit->alloc_areas; i++) {
    UnmapPreeditWindow(ic, preedit_area + i);
  }
  return;
}

void
DestroyPreedit(XicCommon ic, XPointer call_data) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  int i;
  PreeditArea preedit_area;

  if (!preedit) return;

  if (preedit->fontset && preedit->need_free_fontset) {
    XFactoryFreeDefaultFontSet (ic->core.im->core.display);
    preedit->need_free_fontset = False;
  }
#if 0
  PreeditDone(ic, NULL);
#else
  _XUnregisterFilter(ic->core.im->core.display, ic->core.focus_window,
		     FilterConfigureNotify, (XPointer)ic);
#endif

  preedit_area = (PreeditArea)(preedit->preedit_areas);
  for (i = 0; i < preedit->alloc_areas; i++) {
    _XUnregisterFilter(ic->core.im->core.display, 
		       preedit_area[i].window,
		       RepaintPreedit, (XPointer)ic);
    _XUnregisterFilter(ic->core.im->core.display, 
		       preedit_area[i].window,
		       FilterKeyPress, (XPointer)ic);
  }

  if (preedit->gc) XFreeGC(ic->core.im->core.display, preedit->gc);
  if (preedit->rgc) XFreeGC(ic->core.im->core.display, preedit->rgc);

  for (i = 0; i < preedit->alloc_areas; i++) {
    if (ic->core.input_style & XIMPreeditNothing) {
      /* check the window is valid before XDestroyWindow() */
      if(IMCheckIMWindow(ic, preedit_area[i].window)) {
        XDestroyWindow(ic->core.im->core.display, preedit_area[i].window);
      }
    }
  }
  if (preedit->preedit_areas) Xfree(preedit->preedit_areas);
  Xfree(preedit);
  ic->gui_icpart->preedit = (PreeditWin)NULL;
  return;
}

void
PreeditCaretAdjustLookupPlacement(XicCommon ic, XPoint * point) {
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditArea preedit_area;
  PreeditChars preedit_chars;
  int	escapement = 0; 
  int vertical = 0;
  int i;

  if (NULL == preedit) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    preedit = (PreeditWin)(ic->gui_icpart->preedit);
  }
  if (NULL != preedit) {
    preedit_area = (PreeditArea)(preedit->preedit_areas);
    preedit_chars = (PreeditChars)&(preedit->preedit_chars);
    for (i = 0; i < preedit->alloc_areas; i++) {
      if (preedit_area[i].active_lines == 0) {
	int char_offset = preedit_area[i].char_offset;
	int char_len = preedit_area[i].char_len;
	if ((char_offset <= preedit_chars->caret_pos) &&
	    (preedit_chars->caret_pos <= (char_offset + char_len))) {
	  if (char_offset != preedit_chars->caret_pos) {
	    escapement = XwcTextEscapement(preedit->fontset,
					   preedit_chars->wchar + 
					   preedit_chars->caret_pos,
					   preedit_chars->caret_pos -
					   char_offset);
	  }
	  vertical = (ic->core.preedit_attr.area.height * i);
	  point->x += escapement;
	  point->y += vertical;
	  TRACE_MESSAGE('p', ("caret_pos=%d escapement=%d vertical=%d\n",
			      preedit_chars->caret_pos, escapement, vertical));
	  return;
	}
      }
    }
    if ((preedit_chars->caret_pos <= 0) ||
	(preedit_chars->wchar_len <= preedit_chars->caret_pos)) {
      return;
    }
    if (0 < preedit_chars->caret_pos) {
      escapement = XwcTextEscapement(preedit->fontset,
				     preedit_chars->wchar,
				     preedit_chars->caret_pos);
    }
    TRACE_MESSAGE('p', ("caret_pos=%d escapement=%d len=%d caret=%d\n",
			preedit_chars->caret_pos, escapement,
			preedit_chars->wchar_len, preedit_chars->caret_pos));
  }
  return;
}

void
PreeditCaretPlacement(XicCommon ic, XPoint * point)
{
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditArea preedit_area;
  PreeditChars preedit_chars;
  int	x = 0; 
  int y = 0;
  int i;

  if (NULL == preedit) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    preedit = (PreeditWin)(ic->gui_icpart->preedit);
  }
  if (NULL != preedit) {
    preedit_area = (PreeditArea)(preedit->preedit_areas);
    preedit_chars = (PreeditChars)&(preedit->preedit_chars);

    if (preedit_area == 0 || preedit_chars == 0 ||
	preedit_chars->wchar == 0) {
      return;
    }
    for (i = 0; i < preedit->alloc_areas; i++) {
      int char_offset = preedit_area[i].char_offset;
      int char_len = preedit_area[i].char_len;

      if (preedit_area[i].active_lines == 0) {
	if ((char_offset <= preedit_chars->caret_pos) &&
	    (preedit_chars->caret_pos <= (char_offset + char_len))) {

	  XFontSetExtents *fse = 0;
	  if (!preedit->fontset) {
	    SetPreeditFont(ic, NULL);
	  }
	  fse = XExtentsOfFontSet(preedit->fontset);

	  if (char_offset != preedit_chars->caret_pos) {
	    x = XwcTextEscapement(preedit->fontset,
				  preedit_chars->wchar + 
				  char_offset,
				  preedit_chars->caret_pos -
				  char_offset);
	  } else {
	    x = 0;
	  }
	  y = (-fse->max_ink_extent.y);
	  XFactoryGetLocationOnScreen(ic->core.im->core.display,
				      preedit_area[i].window,
				      x, y, point);
	  TRACE_MESSAGE('p', ("caret_pos=%d escapement=%d vertical=%d\n",
			      preedit_chars->caret_pos, x, y));
	  return;

	}
      } else {
	PreeditLine line = (PreeditLine)preedit_area[i].lines;
	int j;

	for (j = 0; j < preedit_area[i].active_lines; j++) {
	  if ((line[j].char_offset <= preedit_chars->caret_pos) &&
	      (preedit_chars->caret_pos < (line[j].char_offset + line[j].char_len))) {
	    XFontSetExtents *fse = 0;
	    if (!preedit->fontset) {
	      SetPreeditFont(ic, NULL);
	    }
	    fse = XExtentsOfFontSet(preedit->fontset);
	    if (line[j].char_offset != preedit_chars->caret_pos) {
	      x = XwcTextEscapement(preedit->fontset,
				    preedit_chars->wchar + 
				    line[j].char_offset,
				    preedit_chars->caret_pos -
				    line[j].char_offset);
	    } else {
	      x = 0;
	    }
	    y = ((fse->max_logical_extent.height * j) +
		 (-fse->max_ink_extent.y));
	    XFactoryGetLocationOnScreen(ic->core.im->core.display,
					preedit_area[i].window,
					x, y, point);
	    TRACE_MESSAGE('p', ("caret_pos=%d escapement=%d vertical=%d\n",
				preedit_chars->caret_pos, x, y));
	    return;
	  }
	}
      }
    }
  }
  return;
}

void
PreeditCaretPlacementRelative(XicCommon ic, XPoint * point)
{
  PreeditWin preedit = (PreeditWin)(ic->gui_icpart->preedit);
  PreeditArea preedit_area;
  PreeditChars preedit_chars;
  int	x = 0; 
  int y = 0;
  int i;
  int new_x;
  int new_y;
  Window child;


  if (NULL == preedit) {
    XIC_GUI(ic, change_preedit)((XIC)ic, PREEDIT_CREATE, NULL);
    preedit = (PreeditWin)(ic->gui_icpart->preedit);
  }
  if (NULL != preedit) {
    preedit_area = (PreeditArea)(preedit->preedit_areas);
    preedit_chars = (PreeditChars)&(preedit->preedit_chars);

    for (i = 0; i < preedit->alloc_areas; i++) {
      int char_offset = preedit_area[i].char_offset;
      int char_len = preedit_area[i].char_len;

      if (preedit_area[i].active_lines == 0) {
	if ((char_offset <= preedit_chars->caret_pos) &&
	    (preedit_chars->caret_pos <= (char_offset + char_len))) {
	  XFontSetExtents *fse = 0;
	  if (0 == preedit_chars->wchar_len) {
	    if (XIMP_CHK_PREAREAMASK(ic)) {
	      x = ic->core.preedit_attr.area.x;
	      y = ic->core.preedit_attr.area.y;
	    } else if (XIMP_CHK_PRESPOTLMASK(ic)) {
	      x = ic->core.preedit_attr.spot_location.x;
	      y = ic->core.preedit_attr.spot_location.y;
	    } else {
	      x  = y = 0;
	    }
	    return;
	  }
	  if (!preedit->fontset) {
	    SetPreeditFont(ic, NULL);
	  }
	  fse = XExtentsOfFontSet(preedit->fontset);

	  if (char_offset != preedit_chars->caret_pos) {
	    x = XwcTextEscapement(preedit->fontset,
				  preedit_chars->wchar + 
				  char_offset,
				  preedit_chars->caret_pos -
				  char_offset);
	  } else {
	    x = 0;
	  }
	  y = fse->max_logical_extent.height;
	  y += (fse->max_ink_extent.height + fse->max_ink_extent.y);

	  XTranslateCoordinates(ic->core.im->core.display,
				preedit_area[i].window,
				ic->core.focus_window,
				x, y, &new_x, &new_y, &child);
	  point->x = new_x;
	  point->y = new_y;

	  TRACE_MESSAGE('p', ("caret_pos=%d escapement=%d vertical=%d\n",
			      preedit_chars->caret_pos, x, y));
	  return;

	}
      } else {
	PreeditLine line = (PreeditLine)preedit_area[i].lines;
	int j;

	for (j = 0; j < preedit_area[i].active_lines; j++) {
	  if ((line[j].char_offset <= preedit_chars->caret_pos) &&
	      (preedit_chars->caret_pos < (line[j].char_offset + line[j].char_len))) {
	    XFontSetExtents *fse = 0;
	    if (0 == preedit_chars->wchar_len) {
	      if (XIMP_CHK_PREAREAMASK(ic)) {
		x = ic->core.preedit_attr.area.x;
		y = ic->core.preedit_attr.area.y;
	      } else if (XIMP_CHK_PRESPOTLMASK(ic)) {
		x = ic->core.preedit_attr.spot_location.x;
		y = ic->core.preedit_attr.spot_location.y;
	      } else {
		x  = y = 0;
	      }
	      return;
	    }
	    if (!preedit->fontset) {
	      SetPreeditFont(ic, NULL);
	    }
	    fse = XExtentsOfFontSet(preedit->fontset);
	    if (line[j].char_offset != preedit_chars->caret_pos) {
	      x = XwcTextEscapement(preedit->fontset,
				    preedit_chars->wchar + 
				    line[j].char_offset,
				    preedit_chars->caret_pos -
				    line[j].char_offset);
	    } else {
	      x = 0;
	    }
	    y = (fse->max_logical_extent.height * (j + 1));
	    y += (fse->max_ink_extent.height + fse->max_ink_extent.y);

	    XTranslateCoordinates(ic->core.im->core.display,
				  preedit_area[i].window,
				  ic->core.focus_window,
				  x, y, &new_x, &new_y, &child);
	    point->x = new_x;
	    point->y = new_y;

	    TRACE_MESSAGE('p', ("caret_pos=%d escapement=%d vertical=%d\n",
				preedit_chars->caret_pos, x, y));
	    return;
	  }
	}
      }
    }
  }
  return;
}
