diff lib/Canna.c @ 0:92745d501b9a

initial import from kinput2-v3.1
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 08 Mar 2010 04:44:30 +0900
parents
children 56c98768f86b 1f1719e33c62
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/Canna.c	Mon Mar 08 04:44:30 2010 +0900
@@ -0,0 +1,1854 @@
+/*
+ * Copyright (c) 1990  Software Research Associates, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, 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 Software Research Associates not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  Software Research
+ * Associates makes no representations about the suitability of this software
+ * for any purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * Author:  Makoto Ishisone, Software Research Associates, Inc., Japan
+ */
+
+/* Copyright 1991 NEC Corporation, Tokyo, Japan.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * 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 NEC Corporation
+ * not be used in advertising or publicity pertaining to distribution
+ * of the software without specific, written prior permission.  NEC 
+ * Corporation makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * NEC CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 
+ * NO EVENT SHALL NEC CORPORATION 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 TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
+ * PERFORMANCE OF THIS SOFTWARE. 
+ *
+ * Author: Akira Kon, NEC Corporation.  (kon@d1.bs2.mt.nec.co.jp)
+ */
+
+/* $BD>$5$J$1$l$P$J$i$J$$$H$3$m(B
+
+ $B!&(BDestroy $B$,8F$P$l$J$$$N$G(B CloseUIContext $B$G$-$J$$!#(B
+ $B!&%b!<%INN0h$NBg$-$5!#(B($B$3$l$OB>$N%U%!%$%k$@$m$&$J(B)
+
+ */
+
+#ifndef lint
+static char *rcsid = "$Id: Canna.c,v 1.55 1999/05/25 08:13:03 ishisone Exp $";
+#endif
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Xmu/Atoms.h>
+#define XK_KATAKANA
+#include <X11/keysym.h>
+#if XtSpecificationRelease > 4
+#include <X11/Xfuncs.h>
+#endif
+#include "CannaP.h"
+#include "DebugPrint.h"
+
+#define _WCHAR_T /* $B$3$NDj5A$O(B jrkanji.h $B$G(B wcKanjiStatus $B$J$I$rDj5A$9$k$?$a(B */
+#define wchar_t wchar
+
+#include <canna/jrkanji.h>
+
+static XtResource resources[] = {
+#define offset(field) XtOffset(CannaObject, canna.field)
+    { XtNcannahost, XtCCannahost, XtRString, sizeof(String),
+	offset(cannahost), XtRString, NULL },
+    { XtNcannafile, XtCCannafile, XtRString, sizeof(String),
+	offset(cannafile), XtRString, NULL },
+    { XtNsendReturnByString, XtCSendReturnByString,
+	XtRBoolean, sizeof(Boolean),
+	offset(sendReturnByString), XtRBoolean, False },
+#undef offset
+};
+
+static void ClassInitialize();
+static void Initialize(), Destroy();
+static Boolean SetValues();
+static int InputEvent();
+static ICString *GetMode();
+static int CursorPos();
+static int NumSegments();
+static ICString *GetSegment();
+static int CompareSegment();
+static ICString *GetItemList();
+static int SelectItem();
+static int ConvertedString();
+static int ClearConversion();
+static void displayPreEdit();
+static int GetTriggerKeys();
+static int PreeditString();
+static int StatusString();
+
+static initializeCannaConnection();
+static toJapaneseMode();
+static void convend();
+static changeTextForCanna();
+static copyInWchar();
+static fixProcForCanna();
+static shiftRight(), shiftLeft(), shiftLeftAll();
+static ibufInitialize(), freeIBuf();
+
+#ifdef KC_SETLISTCALLBACK
+#ifdef CANNA_LIST_Convert
+#define CANNA_LISTFUNCTYPE int
+#else
+#define CANNA_LISTFUNCTYPE void
+#endif
+static CANNA_LISTFUNCTYPE listfunc();
+
+static void openSelection();
+#define SELECTION_SET 0 /* SelectionStart $B$r$7$F$bNI$$$H8@$&>pJs$r@_Dj$9$k(B */
+#define SELECTION_DO  1 /* $B<B:]$K(B SelectionStart $B$r3+;O$9$k(B */
+#else /* !KC_SETLISTCALLBACK */
+#define openSelection(x, y, z)
+#endif /* !KC_SETLISTCALLBACK */
+
+static ICString *GetAuxSegments();
+
+CannaClassRec cannaClassRec = {
+  { /* object fields */
+    /* superclass		*/	(WidgetClass) &inputConvClassRec,
+    /* class_name		*/	"Canna",
+    /* widget_size		*/	sizeof(CannaRec),
+    /* class_initialize		*/	ClassInitialize,
+    /* class_part_initialize	*/	NULL,
+    /* class_inited		*/	FALSE,
+    /* initialize		*/	Initialize,
+    /* initialize_hook		*/	NULL,
+    /* obj1			*/	NULL,
+    /* obj2			*/	NULL,
+    /* obj3			*/	0,
+    /* resources		*/	resources,
+    /* num_resources		*/	XtNumber(resources),
+    /* xrm_class		*/	NULLQUARK,
+    /* obj4			*/	FALSE,
+    /* obj5			*/	FALSE,
+    /* obj6			*/	FALSE,
+    /* obj7			*/	FALSE,
+    /* destroy			*/	Destroy,
+    /* obj8			*/	NULL,
+    /* obj9			*/	NULL,
+    /* set_values		*/	SetValues,
+    /* set_values_hook		*/	NULL,
+    /* obj10			*/	NULL,
+    /* get_values_hook		*/	NULL,
+    /* obj11			*/	NULL,
+    /* version			*/	XtVersion,
+    /* callback_private		*/	NULL,
+    /* obj12			*/	NULL,
+    /* obj13			*/	NULL,
+    /* obj14			*/	NULL,
+    /* extension		*/	NULL
+  },
+  { /* inputConv fields */
+    /* InputEvent		*/	InputEvent,
+    /* GetMode			*/	GetMode,
+    /* CursorPos		*/	CursorPos,
+    /* NumSegments		*/	NumSegments,
+    /* GetSegment		*/	GetSegment,
+    /* CompareSegment		*/	CompareSegment,
+    /* GetItemList		*/	GetItemList,
+    /* SelectItem		*/	SelectItem,
+    /* GetConvertedString	*/	ConvertedString,
+    /* ClearConversion		*/	ClearConversion,
+    /* GetAuxSegments		*/	GetAuxSegments,
+    /* SupportMultipleObjects	*/	True,
+    /* GetTriggerKeys		*/	GetTriggerKeys,
+    /* num_trigger_keys		*/	0,
+    /* trigger_keys		*/	NULL,
+    /* GetPreeditString		*/	PreeditString,
+    /* GetStatusString		*/	StatusString,
+    /* NoMoreObjects		*/	False,
+  },
+  { /* canna fields */
+    /* foo			*/	0,
+  }
+};
+
+WidgetClass cannaObjectClass = (WidgetClass)&cannaClassRec;
+
+static void fix();
+
+static ICString *SymbolList;
+static int NumSymbols;
+
+static void addObject();
+static void deleteObject();
+
+static int checkIfFunctionalChar();
+
+static int ignoreListfunc = 0;
+
+static Display *displaybell = (Display *)0;
+
+static void
+ClassInitialize()
+{
+  TRACE(("CannaObjectClass initialized\n"));
+  /* $B2?$b$7$J$$(B */
+}
+
+static int
+XKanaLookup(event_struct, buffer_return, bytes_buffer,
+	    keysym, status_return)
+XKeyEvent *event_struct;
+char *buffer_return;
+int bytes_buffer;
+KeySym *keysym;
+XComposeStatus *status_return;
+{
+  int res;
+  res = XLookupString(event_struct, buffer_return, bytes_buffer,
+		      keysym, status_return);
+  if (!res && XK_overline <= *keysym && *keysym <= XK_semivoicedsound) {
+    buffer_return[0] = (unsigned long)(*keysym) & 0xff;
+    res = 1;
+  }
+  return res;
+}
+
+static int
+InputEvent(w, event)
+Widget w;
+XEvent *event;
+{
+    CannaObject obj = (CannaObject)w;
+    wchar buf[1024];
+    char kanabuf[20];
+    KeySym ks;
+    XComposeStatus   compose_status;
+    wcKanjiStatus    kanji_status;
+    int len, nbytes, functionalChar;
+
+    /* KeyPress$B0J30$O<N$F$k(B */
+    if (event->type != KeyPress /*&& event->type != KeyRelease*/) return 0;
+
+    obj->canna.textchanged = False;
+
+    /* $B<h$j$"$($:J8;z$KD>$7$F$7$^$&(B */
+    kanabuf[0] = '\0';
+    nbytes = XKanaLookup(event, kanabuf, 20, &ks, &compose_status);
+
+    buf[0] = (wchar)kanabuf[0]; /* $B$-$?$J$$(B */
+
+    if (ks == XK_space && (event->xkey.state & ShiftMask)) {
+      void convend();
+
+      convend(obj);
+      return 0;
+    }
+
+    /* $BD9$5$,%<%m$N$b$N$K4X$7$F$O$A$g$C$H5_:Q(B */
+    functionalChar = checkIfFunctionalChar(event, ks, buf, 1024);
+    /* shift+$B"*$N$h$&$JJ8;z$@$C$?$i#1J8;z$H?t$($k(B */
+    if ( !nbytes && functionalChar ) {
+      nbytes = 1;
+    }
+
+    /* $BMW$i$J$$>l9g$OL5;k$9$k(B */
+    if (nbytes == 0) return 1;
+
+    /* $B%Y%k$rLD$i$9%G%#%9%W%l%$$N@_Dj(B */
+ 
+    displaybell = XtDisplayOfObject((Widget)obj);
+
+    /* $B$+$J4A;zJQ49$9$k(B */
+    len = wcKanjiString((int)obj, (int)buf[0],
+			(wchar_t *)buf, 1024, &kanji_status);
+
+    displayPreEdit(obj, len, buf, &kanji_status);
+    return (kanji_status.info & KanjiThroughInfo) ? 1 : 0;
+}
+
+static void
+displayPreEdit(obj, len, buf, ks)
+CannaObject obj;
+int len;
+wchar *buf;
+wcKanjiStatus *ks;
+{
+    Widget w = (Widget)obj;
+
+    if (len > 0) { /* $B3NDjJ8;z$,$"$k>l9g(B */
+      if (len == 1 && (ks->info & KanjiThroughInfo) &&
+	  (obj->canna.sendReturnByString == False ||
+	   (((buf[0] & 0x7f) < (unsigned)0x20 &&
+	     buf[0] != '\r' && buf[0] != '\t') ||
+	    buf[0] == '\177'))) {
+	;/* XSendEvent$B$GAw$C$F$b$i$&$h$&$JJ8;z(B($B2?$b$7$J$$(B) */
+      }
+      else {
+	ks->info &= ~KanjiThroughInfo;
+	fixProcForCanna(obj, buf, len);
+	fix(obj);
+      }
+    }
+
+    changeTextForCanna(obj, ks);
+
+    /* $B%F%-%9%H$NJQ2=$r%A%'%C%/$9$k(B */
+    if (obj->canna.textchanged) {
+	XtCallCallbackList(w, obj->inputConv.textchangecallback,
+			   (XtPointer)NULL);
+	obj->canna.textchanged = False;
+    }
+
+    /* $BF~NO%b!<%I$r%A%'%C%/$9$k(B */
+    if (ks->info & KanjiModeInfo) {
+      XtCallCallbackList(w, obj->inputConv.modechangecallback,
+			 (XtPointer)NULL);
+    }
+
+    if (ks->info & KanjiGLineInfo) { /* $B0lMw9T$,$"$k>l9g(B */
+      ICAuxControlArg arg;
+
+      switch (obj->canna.ibuf->candstat) {
+      case CANNA_GLINE_Start:
+	arg.command = ICAuxStart;
+	XtCallCallbackList((Widget)obj, obj->inputConv.auxcallback,
+			   (XtPointer)&arg);
+	break;
+      case CANNA_GLINE_End:
+	arg.command = ICAuxEnd;
+	XtCallCallbackList((Widget)obj, obj->inputConv.auxcallback,
+			   (XtPointer)&arg);
+	break;
+      case CANNA_GLINE_Change:
+	arg.command = ICAuxChange;
+	XtCallCallbackList((Widget)obj, obj->inputConv.auxcallback,
+			   (XtPointer)&arg);
+	break;
+      }
+    }
+
+    /* $B=*$o$j$+$I$&$+%A%'%C%/$9$k(B */
+    if (ks->info & KanjiModeInfo) {
+      wchar modeinfo[4];
+
+      wcKanjiControl((int)obj, KC_SETMODEINFOSTYLE, (char *)1);
+      wcKanjiControl((int)obj, KC_QUERYMODE, (char *)modeinfo);
+      wcKanjiControl((int)obj, KC_SETMODEINFOSTYLE, (char *)0);
+      if (modeinfo[0] - '@' == CANNA_MODE_AlphaMode) {
+	toJapaneseMode(obj);
+	convend(obj);
+      }
+    }
+    openSelection(obj, SELECTION_DO, 0/* dummy */);
+}
+
+static ICString *
+GetMode(w)
+Widget w;
+{
+    CannaObject obj = (CannaObject)w;
+    wchar *mode;
+    static ICString icstr;
+
+    icstr.nchars = obj->canna.ibuf->modelen;
+    if (icstr.nchars > 0) {
+      mode = obj->canna.ibuf->curmode;
+      icstr.data = (char *)mode;
+      icstr.nbytes = icstr.nchars * sizeof(wchar);
+      icstr.attr = ICAttrNormalString;
+    }
+    return &icstr;
+}
+
+static int
+CursorPos(w, nsegp, ncharp)
+Widget w;
+Cardinal *nsegp;
+Cardinal *ncharp;
+{
+    CannaObject obj = (CannaObject)w;
+    iBuf *ib = obj->canna.ibuf;
+    Cardinal nseg, nchar;
+    int ret = 0;
+
+    if (ib->curseg < ib->nseg) {
+      nseg = ib->curseg;
+      nchar = 0;
+    }
+    else {
+      nseg = ib->offset;
+      nchar = ib->len[0 + ib->offset];
+      ret = 1;
+    }
+    if (nsegp) {
+      *nsegp = nseg;
+    }
+    if (ncharp) {
+      *ncharp = nchar;
+    }
+
+    return ret;
+}
+
+static int
+NumSegments(w)
+Widget w;
+{
+    CannaObject obj = (CannaObject)w;
+    iBuf *ib = obj->canna.ibuf;
+
+    return ib->nseg;
+}
+
+static ICString *
+GetSegment(w, n)
+Widget w;
+Cardinal n;
+{
+    CannaObject obj = (CannaObject)w;
+    iBuf *ib = obj->canna.ibuf;
+    static ICString seg;
+
+    seg.data = (char *)ib->str[n];
+    seg.nchars = ib->len[n];
+    seg.nbytes = seg.nchars * sizeof(wchar);
+    if (ib->curseg == n) {
+      seg.attr = ICAttrConverted | ICAttrCurrentSegment;
+    }
+    else if (n < ib->offset) {
+      seg.attr = ICAttrConverted;
+    }
+    else {
+      seg.attr = ICAttrNotConverted;
+    }
+
+    return &seg;
+}
+
+/* ARGSUSED */
+static int
+CompareSegment(w, seg1, seg2, n)
+Widget w;
+ICString *seg1;
+ICString *seg2;
+Cardinal *n;
+{
+    wchar *p, *q;
+    int len, nsame;
+    int result = 0;
+
+    if (seg1->attr != seg2->attr) result |= ICAttrChanged;
+
+    len = seg1->nchars > seg2->nchars ? seg2->nchars : seg1->nchars;
+    nsame = 0;
+    p = (wchar *)seg1->data;
+    q = (wchar *)seg2->data;
+    while (nsame < len && *p++ == *q++) nsame++;
+
+    if (nsame != len || len != seg1->nchars || len != seg2->nchars)
+	result |= ICStringChanged;
+
+    if (n) *n = nsame;
+
+    return result;
+}
+
+static ICString *
+GetItemList(w, n)
+Widget w;
+Cardinal *n;
+{
+    CannaObject obj = (CannaObject)w;
+
+    *n = obj->canna.numcand;
+    return obj->canna.candlist;
+}
+
+#define SELECTBUFSIZE 1024
+
+static int
+SelectItem(w, n)
+Widget w;
+int n;
+{
+  CannaObject obj = (CannaObject)w;
+  wcKanjiStatus ks;
+  wcKanjiStatusWithValue ksv;
+  wchar buf[SELECTBUFSIZE];
+
+  if (n >= 0)
+    *(obj->canna.cur_addr) = n;
+
+  ksv.ks = &ks;
+  buf[0] = (wchar)'\r';
+  ksv.buffer = buf;
+  ksv.n_buffer = SELECTBUFSIZE;
+  ksv.val = CANNA_FN_Kakutei;
+  {
+    ignoreListfunc = 1;
+    wcKanjiControl((int)obj, KC_DO, (char *)&ksv);
+    ignoreListfunc = 0;
+  }
+  displayPreEdit(obj, ksv.val, buf, ksv.ks);
+  return 0;
+}
+
+static int
+ConvertedString(w, encoding, format, length, string)
+Widget w;
+Atom *encoding;
+int *format;
+int *length;
+XtPointer *string;
+{
+    CannaObject obj = (CannaObject)w;
+    iBuf *ib = obj->canna.ibuf;
+    wchar *wbuf, *wp;
+    int len, wlen;
+    extern int convJWStoCT();
+    int offset = ib->offset;
+
+    if (ib == NULL) return -1;
+
+    if (ib->nseg == 0 || ib->offset == 0) return -1;
+
+    wlen = ib->len[offset - 1];
+    if (wlen == 0) return -1;
+
+    wbuf = ib->str[offset - 1];
+
+    /*
+     * Canna $B%*%V%8%'%/%H$O(B COMPOUND_TEXT $B%(%s%3!<%G%#%s%0$7$+%5%]!<%H$7$J$$(B
+     * COMPOUND_TEXT $B$KJQ49$9$k(B
+     */
+    *encoding = XA_COMPOUND_TEXT(XtDisplayOfObject((Widget)obj));
+    *format = 8;
+
+    /* COMPOUND_TEXT $B$O(B \r $B$,Aw$l$J$$$N$G(B \n $B$KJQ49$7$F$*$/(B */
+    for (wp = wbuf; *wp != 0; wp++) {
+	if (*wp == '\r') *wp = '\n';
+    }
+
+    *length = len = convJWStoCT(wbuf, (unsigned char *)NULL, 0);
+    *string = XtMalloc(len + 1);
+    (void)convJWStoCT(wbuf, (unsigned char *)*string, 0);
+
+    shiftLeftAll(ib);
+
+    return 0;
+}
+
+static int
+ClearConversion(w)
+Widget w;
+{
+  CannaObject obj = (CannaObject)w;
+
+  wcKanjiStatus ks;
+  wcKanjiStatusWithValue ksv;
+  wchar buf[SELECTBUFSIZE];
+
+  ksv.ks = &ks;
+  ksv.buffer = buf;
+  ksv.n_buffer = SELECTBUFSIZE;
+  wcKanjiControl((int)obj, KC_KILL, (char *)&ksv);
+  displayPreEdit(obj, ksv.val, buf, ksv.ks);
+  return 0;
+}
+
+static ICString *
+GetAuxSegments(w, n, ns, nc)
+Widget w;
+Cardinal *n, *ns, *nc;
+{
+  CannaObject obj = (CannaObject)w;
+  iBuf *ib = obj->canna.ibuf;
+  Cardinal nseg, nchar;
+
+  if (n) {
+    *n = ib->ngseg;
+  }
+
+  if (ib->curgseg < ib->ngseg) {
+    nseg = ib->curgseg;
+    nchar = 0;
+  }
+  else {
+    nseg = 0;
+    nchar = ib->gllen[0];
+  }
+  if (ns) {
+    *ns = nseg;
+  }
+  if (nc) {
+    *nc = nchar;
+  }
+
+  return ib->ics;
+}
+
+/* ARGSUSED */
+static void
+Initialize(req, new, args, num_args)
+Widget req;
+Widget new;
+ArgList args;
+Cardinal *num_args;
+{
+    CannaObject obj = (CannaObject)new;
+
+    obj->canna.selectionending = False;
+    obj->canna.textchanged = False;
+    obj->canna.symbollist = SymbolList;
+    obj->canna.numsymbols = NumSymbols;
+    obj->canna.cursymbol = 0;
+    obj->canna.candlist = NULL;
+    obj->canna.candlistsize = 0;
+    obj->canna.numcand = 0;
+    obj->canna.lastTextLengthIsZero = False;
+      
+    ibufInitialize(obj);
+
+    /* $BJQ49$N=i4|2=(B */
+    initializeCannaConnection(obj);
+
+    addObject(obj);
+}
+
+static int
+bell()
+{
+  if (displaybell) {
+    XBell(displaybell, 0);
+  }
+  return 0;
+}
+
+static int nCannaContexts = 0;
+
+static
+initializeCannaConnection(obj)
+CannaObject obj;
+{
+  char **warn = 0;
+  extern (*jrBeepFunc)();
+
+  if (nCannaContexts == 0) {
+#ifdef KC_SETSERVERNAME
+    if (obj->canna.cannahost != NULL) {
+      wcKanjiControl((int)obj, KC_SETSERVERNAME, obj->canna.cannahost);
+    }
+#endif /* KC_SETSERVERNAME */
+#ifdef KC_SETINITFILENAME
+    if (obj->canna.cannafile != NULL) {
+      wcKanjiControl((int)obj, KC_SETINITFILENAME, obj->canna.cannafile);
+    }
+#endif /* KC_SETINITFILENAME */
+  
+    wcKanjiControl((int)obj, KC_INITIALIZE, (char *)&warn);
+    TRACE(("Canna initialized\n"));
+
+    if (warn) {
+      char **p;
+    
+      for (p = warn; *p ; p++) {
+	XtAppWarning(XtWidgetToApplicationContext((Widget)obj), *p);
+      }
+    }
+  }
+  nCannaContexts++;
+
+#ifdef KC_SETAPPNAME
+  wcKanjiControl((int)obj, KC_SETAPPNAME, "kinput2");
+#endif
+
+#ifdef DONT_USE_HANKAKU_KATAKANA
+  /* $BH>3Q%+%?%+%J$N6X;_(B */
+  wcKanjiControl((int)obj, KC_INHIBITHANKAKUKANA, (char *)1);
+#endif
+  
+  /* $B0lMw9T$r#7#8%3%i%`$K@_Dj(B */
+  wcKanjiControl((int)obj, KC_SETWIDTH, (char *)78);
+
+  /* $B8uJd0lMw%3!<%k%P%C%/$r@_Dj(B */
+
+#ifdef KC_SETLISTCALLBACK
+  {
+    jrListCallbackStruct lcs;
+
+    lcs.client_data = (char *)obj;
+    lcs.callback_func = listfunc;
+    wcKanjiControl((int)obj, KC_SETLISTCALLBACK, (char *)&lcs);
+  }
+#endif /* KC_SETLISTCALLBACK */
+
+  jrBeepFunc = bell;
+
+  /* $B$5$i$KF|K\8lF~NO%b!<%I$K$7$F$*$/(B */
+  toJapaneseMode(obj);
+}
+
+
+static void
+Destroy(w)
+Widget w;
+{
+    CannaObject obj = (CannaObject)w;
+    wcKanjiStatusWithValue ksv;
+    wcKanjiStatus ks;
+    wchar buf[512];
+    int i;
+    
+    /* $B%P%C%U%!$N2rJ|(B */
+
+    freeIBuf(obj->canna.ibuf);
+
+    if (obj->canna.candlist) {
+      for (i = 0 ; i < obj->canna.candlistsize ; i++) {
+	if ((obj->canna.candlist + i)->data) {
+	  XtFree((obj->canna.candlist + i)->data);
+	}
+      }
+
+      XtFree((char *)obj->canna.candlist);
+    }
+
+    /* $B!X$+$s$J!Y$N=hM}(B */
+    ksv.buffer = buf;
+    ksv.n_buffer = 512;
+    ksv.ks = &ks;
+
+    wcKanjiControl((int)obj, KC_CLOSEUICONTEXT, (char *)&ksv);
+
+    if (--nCannaContexts == 0) {
+      char **warn = 0;
+
+      wcKanjiControl(0, KC_FINALIZE, (char *)&warn);
+      if (warn) {
+	char **p;
+	
+	for (p = warn; *p ; p++) {
+	  XtAppWarning(XtWidgetToApplicationContext((Widget)obj), *p);
+	}
+      }
+    }
+
+    deleteObject(obj);
+}
+
+static Boolean
+SetValues(cur, req, wid, args, num_args)
+Widget cur;
+Widget req;
+Widget wid;
+ArgList args;
+Cardinal *num_args;
+/* ARGSUSED */
+{
+    CannaObject old = (CannaObject)cur;
+    CannaObject new = (CannaObject)wid;
+
+    return False;	     
+}
+
+static void
+fix(obj)
+CannaObject obj;
+{
+    /* $B3NDj$N=hM}(B */
+    XtCallCallbackList((Widget)obj, obj->inputConv.fixcallback,
+		       (XtPointer)NULL);	/* $B!)!)!)(B */
+}
+
+static void
+convend(obj)
+CannaObject obj;
+{
+    XtCallCallbackList((Widget)obj, obj->inputConv.endcallback,
+		       (XtPointer)NULL);
+}
+
+/*
+ * keeping list of objects
+ */
+typedef struct _oblist_ {
+    CannaObject obj;
+    struct _oblist_ *next;
+} ObjRec;
+
+static ObjRec *ObjList = NULL;
+
+static void
+addObject(obj)
+CannaObject obj;
+{
+    ObjRec *objp = XtNew(ObjRec);
+
+    objp->obj = obj;
+    objp->next = ObjList;
+    ObjList = objp;
+}
+
+static void
+deleteObject(obj)
+CannaObject obj;
+{
+    ObjRec *objp, *objp0;
+
+    for (objp0 = NULL, objp = ObjList;
+	 objp != NULL;
+	 objp0 = objp, objp = objp->next) {
+	if (objp->obj == obj) {
+	    if (objp0 == NULL) {
+		ObjList = objp->next;
+	    } else {
+		objp0->next = objp->next;
+	    }
+	    XtFree((char *)objp);
+	    return;
+	}
+    }
+}
+
+/*
+ * Operations to canna.ibuf
+ */
+
+/* cfuncdef
+
+   changeTextForCanna -- ibuf $B$NFbMF$r(B kanji_status $B$rMQ$$$F=q$-49$($k!#(B
+
+
+   $B4pK\E*$K$O8uJdJ8;zNs$H8uJd0lMw9TJ8;zNs$K4X$7$F0J2<$N=hM}$r9T$&!#(B
+
+   (1) $B$$$:$l$NJ8;zNs$b%F%-%9%H$,H?E>$7$F$$$kItJ,$H$=$&$G$J$$ItJ,(B
+       $B$,B8:_$7!"H?E>$7$F$$$kItJ,$O#1%+=j$7$+B8:_$7$J$$!#(B
+   (2) $B$7$?$,$C$F$$$:$l$NJ8;zNs$bH?E>$7$F$$$k$H$3$m$H$=$NN>C<$KH?E>(B
+       $B$7$F$$$J$$ItJ,$H$,B8:_$9$k>l9g$K#3ItJ,$KJ,$+$l$k$3$H$K$J$j!"(B
+       $BH?E>$7$F$$$kItJ,$N0LCV$d!"H?E>$7$F$$$k2U=j$,$J$$>l9g$J$I$r(B
+       $B9g$o$;$F9M$($F$b#3ItJ,0J>e$KJ,$+$l$k$3$H$O$J$$!#(B
+   (3) $B$7$?$,$C$F!"$$$:$l$NJ8;zNs$b:GBg#3$D$N%;%0%a%s%H$KJ,$1$FI=<((B
+       $B%&%#%8%'%C%H$KEO$9$h$&$K$9$k!#(B
+
+ */
+
+static
+changeTextForCanna(cldata, ksp)
+caddr_t cldata;
+wcKanjiStatus *ksp;
+{
+    CannaObject obj = (CannaObject)cldata;
+    iBuf *ibuf = obj->canna.ibuf;
+    int offset = ibuf->offset, i;
+
+    if (ksp->length == 0) {
+      ibuf->curseg = offset;
+      ibuf->nseg = offset;
+      ibuf->len[0 + offset] = ibuf->len[1 + offset] =
+	ibuf->len[2 + offset] = 0;
+      if (!obj->canna.lastTextLengthIsZero) {
+	obj->canna.textchanged = True;
+	obj->canna.lastTextLengthIsZero = True;
+      }
+    }
+    else if (ksp->length > 0) {
+      obj->canna.lastTextLengthIsZero = False;
+      ibuf->len[1 + offset] = ibuf->len[2 + offset] = 0;
+      if (ksp->revLen > 0) {
+	if (ksp->revPos == 0) {
+	  int remain = ksp->length - ksp->revLen;
+
+	  copyInWchar(ksp->echoStr, ksp->revLen,
+		      &(ibuf->str[0 + offset]), &(ibuf->size[0 + offset]),
+		      &(ibuf->len[0 + offset]));
+	  ibuf->curseg = offset;
+	  ibuf->nseg = offset + 1;
+	  if (remain) {
+	    copyInWchar(ksp->echoStr + ksp->revLen, remain,
+			&(ibuf->str[1 + offset]), &(ibuf->size[1 + offset]),
+			&(ibuf->len[1 + offset]));
+	    ibuf->nseg = offset + 2;
+	  }
+	}
+	else {
+	  int remain = ksp->length - ksp->revPos - ksp->revLen;
+
+	  copyInWchar(ksp->echoStr, ksp->revPos,
+		      &(ibuf->str[0 + offset]), &(ibuf->size[0 + offset]),
+		      &(ibuf->len[0 + offset]));
+	  copyInWchar(ksp->echoStr + ksp->revPos, ksp->revLen,
+		      &(ibuf->str[1 + offset]), &(ibuf->size[1 + offset]),
+		      &(ibuf->len[1 + offset]));
+	  ibuf->curseg = offset + 1;
+	  ibuf->nseg = offset + 2;
+	  if (remain) {
+	    copyInWchar(ksp->echoStr + ksp->revPos + ksp->revLen,
+			remain,
+			&(ibuf->str[2 + offset]), &(ibuf->size[2 + offset]),
+			&(ibuf->len[2 + offset]));
+	  ibuf->nseg = offset + 3;
+	  }
+	}
+      }
+      else {
+	copyInWchar(ksp->echoStr, ksp->length,
+		    &(ibuf->str[0 + offset]), &(ibuf->size[0 + offset]),
+		    &(ibuf->len[0 + offset]));
+	ibuf->len[1 + offset] = ibuf->len[2 + offset] = 0;
+	ibuf->nseg = offset + 1;
+	ibuf->curseg = offset + 1;
+      }
+      obj->canna.textchanged = True;
+    }
+    /* $B%b!<%I(B */
+    if (ksp->info & KanjiModeInfo) {
+      int modelen = 0;
+
+      while (ksp->mode[modelen]) modelen++;
+      copyInWchar(ksp->mode, modelen,
+		  &(ibuf->curmode), &(ibuf->modesize), &(ibuf->modelen));
+    }
+    /* $B0lMw9T(B */
+    if (ksp->info & KanjiGLineInfo) {
+      if (ksp->gline.length == 0) {
+	switch (ibuf->candstat) {
+	case CANNA_GLINE_Empty:
+	case CANNA_GLINE_End:
+	  ibuf->candstat = CANNA_GLINE_Empty;
+	  break;
+	case CANNA_GLINE_Start:
+	case CANNA_GLINE_Change:
+	  ibuf->candstat = CANNA_GLINE_End;
+	  break;
+	}
+	ibuf->curgseg = 0;
+	ibuf->ngseg = 0;
+	ibuf->gllen[0] = ibuf->gllen[1] = ibuf->gllen[2] = 0;
+      }
+      else if (ksp->gline.length > 0) {
+	switch (ibuf->candstat) {
+	case CANNA_GLINE_Empty:
+	case CANNA_GLINE_End:
+	  ibuf->candstat = CANNA_GLINE_Start;
+	  break;
+	case CANNA_GLINE_Start:
+	case CANNA_GLINE_Change:
+	  ibuf->candstat = CANNA_GLINE_Change;
+	  break;
+	}
+	ibuf->gllen[1] = ibuf->gllen[2] = 0;
+	if (ksp->gline.revLen > 0) {
+	  if (ksp->gline.revPos == 0) {
+	    int remain = ksp->gline.length - ksp->gline.revLen;
+	    
+	    copyInWchar(ksp->gline.line, ksp->gline.revLen,
+			&(ibuf->gline[0]), &(ibuf->glsize[0]),
+			&(ibuf->gllen[0]));
+	    ibuf->curgseg = 0;
+	    ibuf->ngseg = 1;
+	    if (remain) {
+	      copyInWchar(ksp->gline.line + ksp->gline.revLen, remain,
+			  &(ibuf->gline[1]), &(ibuf->glsize[1]),
+			  &(ibuf->gllen[1]));
+	      ibuf->ngseg = 2;
+	    }
+	  }
+	  else {
+	    int remain = ksp->gline.length
+	      - ksp->gline.revPos - ksp->gline.revLen;
+	    
+	    copyInWchar(ksp->gline.line, ksp->gline.revPos,
+			&(ibuf->gline[0]), &(ibuf->glsize[0]),
+			&(ibuf->gllen[0]));
+	    copyInWchar(ksp->gline.line + ksp->gline.revPos, ksp->gline.revLen,
+			&(ibuf->gline[1]), &(ibuf->glsize[1]),
+			&(ibuf->gllen[1]));
+	    ibuf->curgseg = 1;
+	    ibuf->ngseg = 2;
+	    if (remain) {
+	      copyInWchar(ksp->gline.line
+			  + ksp->gline.revPos + ksp->gline.revLen,
+			  remain,
+			  &(ibuf->gline[2]), &(ibuf->glsize[2]),
+			  &(ibuf->gllen[2]));
+	      ibuf->ngseg = 3;
+	    }
+	  }
+	}
+	else {
+	  copyInWchar(ksp->gline.line, ksp->gline.length,
+		      &(ibuf->gline[0]), &(ibuf->glsize[0]),
+		      &(ibuf->gllen[0]));
+	  ibuf->gllen[1] = ibuf->gllen[2] = 0;
+	  ibuf->ngseg = 1;
+	  ibuf->curgseg = 1;
+	}
+      }
+      for (i = 0 ; i < ibuf->ngseg ; i++) {
+	ibuf->ics[i].data = (char *)ibuf->gline[i];
+	ibuf->ics[i].nchars = ibuf->gllen[i];
+	ibuf->ics[i].nbytes = ibuf->gllen[i] * sizeof(wchar);
+	ibuf->ics[i].attr = ICAttrConverted;
+      }
+      if (ibuf->curgseg < ibuf->ngseg) {
+	ibuf->ics[ibuf->curgseg].attr |= ICAttrCurrentSegment;
+      }
+    }
+}
+
+/* cfuncdef
+
+   copyInWchar -- wchar $B$r%3%T!<$9$k!#(B
+
+   ws, wlen $B$G<($5$l$?(B wchar $BJ8;zNs$r(B wsbuf $B$N%]%$%s%H@h$N%P%C%U%!$K3J(B
+   $BG<$9$k!#(Bwsbuf $B$N%5%$%:$O(B wssize $B$N%]%$%s%H@h$K3JG<$5$l$F$$$kCM$G;X(B
+   $BDj$5$l$k$,!"$=$l$G$O>.$5$$;~$O(B copyInWchar $BFb$G(B XtRealloc $B$5$l!"?7(B
+   $B$?$K%"%m%1!<%H$5$l$?%P%C%U%!$,(B wsbuf $B$N%]%$%s%H@h$K3JG<$5$l$k!#$^$?!"(B
+   $B%P%C%U%!$N?7$?$J%5%$%:$,(B wssize $B$N%]%$%s%H@h$K3JG<$5$l$k!#F@$i$l$?(B
+   $BJ8;z?t$,(Bwslen $B$N%]%$%s%H@h$K3JG<$5$l$k!#(B
+
+ */
+
+static
+copyInWchar(ws, wlen, wsbuf, wssize, wslen)
+wchar *ws;
+int wlen;
+wchar **wsbuf;
+int *wssize, *wslen;
+{
+  int i;
+
+  if (*wssize == 0) {
+    if ((*wsbuf = (wchar *)XtMalloc((wlen + 1) * sizeof(wchar)))
+	 == (wchar *)0) {
+      /* $B%(%i!<$@(B */
+    }
+    else {
+      *wssize = wlen + 1;
+    }
+  }
+  if (wlen + 1 > *wssize) {
+    if ((*wsbuf = (wchar *)XtRealloc((char *)*wsbuf,
+				     (wlen + 1) * sizeof(wchar)))
+	 == (wchar *)0) {
+      /* $B%(%i!<$@!#$I$&$9$l$PNI$$!)(B */
+    }
+    else {
+      *wssize = wlen + 1;
+    }
+  }
+  *wslen = wlen;
+  (void)bcopy(ws, *wsbuf, wlen * sizeof(wchar));
+  *(*wsbuf + wlen) = (wchar)0;
+
+  return 0; /* $B#0$OFC$K0UL#$,L5$$(B */
+}
+
+/* cfuncdef
+
+   fixProcForCanna -- $B3NDj$7$?J8;z$N=hM}$r9T$&(B
+
+ */
+
+static
+fixProcForCanna(cldata, fixedstr, fixedlen)
+caddr_t cldata;
+wchar *fixedstr;
+int fixedlen;
+{
+  CannaObject obj = (CannaObject)cldata;
+  iBuf *ib = obj->canna.ibuf;
+  int offset = ib->offset;
+  
+  if (offset < NConvertedSegments) {
+    shiftRight(ib);
+    offset = ib->offset;
+  }
+  else {
+    shiftLeft(ib);
+  }
+  copyInWchar(fixedstr, fixedlen,
+	      &(ib->str[offset - 1]), &(ib->size[offset - 1]),
+	      &(ib->len[offset - 1]));
+}
+
+/* cfuncdef
+
+   shiftRight -- ibuf $B$NJ8;z%P%C%U%!$N(B offset $B0J9_$r1&$K%7%U%H$9$k(B
+
+ */
+
+static
+shiftRight(ib)
+iBuf *ib;
+{
+  int i;
+  wchar *tmpbuf;
+  int tmpsize, tmplen;
+  int offset = ib->offset;
+
+  tmpbuf = ib->str[ib->nseg];
+  tmpsize = ib->size[ib->nseg];
+  for (i = ib->nseg ; offset < i ; i--) {
+    ib->str[i] = ib->str[i - 1];
+    ib->size[i] = ib->size[i - 1];
+    ib->len[i] = ib->len[i - 1];
+  }
+  ib->str[offset] = tmpbuf;
+  ib->size[offset] = tmpsize;
+  ib->offset++;
+  ib->nseg++;
+  ib->curseg++;
+}
+
+/* cfuncdef
+
+   shiftLeft -- ibuf $B$NJ8;z%P%C%U%!$N(B offset $B0JA0$r:8$K%7%U%H$9$k(B
+
+ */
+
+static
+shiftLeft(ib)
+iBuf *ib;
+{
+  int i;
+  wchar *tmpbuf;
+  int tmpsize, tmplen;
+  int offset = ib->offset;
+
+  tmpbuf = ib->str[0];
+  tmpsize = ib->size[0];
+  for (i = 0 ; i < offset - 1 ; i++) {
+    ib->str[i] = ib->str[i + 1];
+    ib->size[i] = ib->size[i + 1];
+    ib->len[i] = ib->len[i + 1];
+  }
+  ib->str[offset - 1] = tmpbuf;
+  ib->size[offset - 1] = tmpsize;
+}
+
+/* cfuncdef
+
+   shiftLeftAll -- ibuf $B$NJ8;z%P%C%U%!A4BN$r:8$K%7%U%H$9$k(B
+
+ */
+
+static
+shiftLeftAll(ib)
+iBuf *ib;
+{
+  int i;
+  wchar *tmpbuf;
+  int tmpsize, tmplen;
+  int nseg = ib->nseg;
+
+  tmpbuf = ib->str[0];
+  tmpsize = ib->size[0];
+  for (i = 0 ; i < nseg - 1 ; i++) {
+    ib->str[i] = ib->str[i + 1];
+    ib->size[i] = ib->size[i + 1];
+    ib->len[i] = ib->len[i + 1];
+  }
+  ib->str[nseg - 1] = tmpbuf;
+  ib->size[nseg - 1] = tmpsize;
+  ib->nseg--;
+  ib->curseg--;
+  ib->offset--;
+}
+
+/* cfuncdef
+
+   ibufInitialize -- ibuf $B$N=i4|2==hM}(B
+ */
+
+static
+ibufInitialize(obj)
+CannaObject obj;
+{
+  int i;
+  iBuf *ib;
+
+  ib = obj->canna.ibuf = (iBuf *)XtMalloc(sizeof(iBuf));
+  if (ib == 0) {
+    /* $B%(%i!<$@!#$I$&$9$Y(B */
+  }
+  for (i = 0 ; i < NConvertedSegments + 3 ; i++) {
+    ib->size[i] = 0;
+    ib->len[i] = 0;
+  }
+  ib->offset = ib->curseg = ib->nseg = 0;
+  ib->candstat = CANNA_GLINE_Empty;
+  for (i = 0 ; i < 3 ; i++) {
+    ib->gline[i] = 0;
+    ib->glsize[i] = ib->gllen[i] = 0;
+  }
+  ib->modesize = 0;
+  ib->modelen = 0;
+}
+
+/* cfuncdef
+
+   freeIBuf -- ibuf $B$N(B free
+
+ */
+
+static
+freeIBuf(ib)
+iBuf *ib;
+{
+  int i;
+
+  if (ib == 0) {
+    return 0;
+  }
+  for (i = 0 ; i < NConvertedSegments + 3 ; i++) {
+    if (ib->size[i] > 0) {
+      XtFree((char *)ib->str[i]);
+    }
+  }
+  for (i = 0 ; i < 3 ; i++) {
+    if (ib->glsize[i] > 0) {
+      XtFree((char *)ib->gline[i]);
+    }
+  }
+  if (ib->modesize > 0) {
+    XtFree((char *)ib->curmode);
+  }
+  free(ib);
+  return 0;
+}
+
+static
+toJapaneseMode(obj)
+CannaObject obj;
+{
+  wcKanjiStatusWithValue ksv;
+  wcKanjiStatus ks;
+  wchar buf[1024];
+
+  buf[0] = '@';
+  ksv.ks = &ks;
+  ksv.buffer = buf;
+  ksv.n_buffer = 1024;
+#ifndef KC_DO
+  ksv.val = CANNA_MODE_HenkanMode;
+  wcKanjiControl((int)obj, KC_CHANGEMODE, (char *)&ksv);
+#else
+  ksv.val = CANNA_FN_JapaneseMode;
+  wcKanjiControl((int)obj, KC_DO, (char *)&ksv);
+#endif
+  changeTextForCanna(obj, &ks);
+}
+
+/* checkIfFunctionalChar -- $B%7%U%H%-!<$N$h$&$KL50UL#$J%-!<$+$I$&$+$NH=JL(B
+
+  $BCM(B:		$B#1(B	$B0UL#$,$"$k(B
+  		$B#0(B	$BL50UL#(B($B%b%G%#%U%!%$%"%-!<$J$I(B)
+*/
+
+static int
+checkIfFunctionalChar(event_struct, keysym, buffer_return, n_buffer)
+XKeyEvent *event_struct;
+KeySym keysym;
+wchar *buffer_return;
+int n_buffer;
+{
+  int functionalChar = 0;
+
+  switch ((int)keysym)
+    {
+    case XK_KP_F1:
+    case XK_KP_F2:
+    case XK_KP_F3:
+    case XK_KP_F4:
+      *buffer_return = 	CANNA_KEY_PF1 + (int)keysym - (int)XK_KP_F1;
+      functionalChar = 1;
+      break;
+    case XK_F1:
+    case XK_F2:
+    case XK_F3:
+    case XK_F4:
+    case XK_F5:
+    case XK_F6:
+    case XK_F7:
+    case XK_F8:
+    case XK_F9:
+    case XK_F10:
+    case XK_F11:
+    case XK_F12:
+    case XK_F13:
+    case XK_F14:
+    case XK_F15:
+    case XK_F16:
+      *buffer_return = CANNA_KEY_F1 + (int)keysym - (int)XK_F1;
+      functionalChar = 1;
+      break;
+    case XK_Insert:
+      *buffer_return = CANNA_KEY_Insert;
+      functionalChar = 1;
+      break;
+    case XK_Prior:
+      *buffer_return = CANNA_KEY_Rollup;
+      functionalChar = 1;
+      break;
+    case XK_Next:
+      *buffer_return = CANNA_KEY_Rolldown;
+      functionalChar = 1;
+      break;
+    case XK_Muhenkan:
+      if (event_struct->state & 4 /* control-shifted */)
+	*buffer_return = CANNA_KEY_Cntrl_Nfer;
+      else if (event_struct->state & 1 /* shifted */)
+	*buffer_return = CANNA_KEY_Shift_Nfer;
+      else
+	*buffer_return = CANNA_KEY_Nfer;
+      functionalChar = 1;
+      break;
+    case XK_Kanji:
+      if (event_struct->state & 4 /* control-shifted */)
+	*buffer_return = CANNA_KEY_Cntrl_Xfer;
+      else if (event_struct->state & 1 /* shifted */)
+	*buffer_return = CANNA_KEY_Shift_Xfer;
+      else
+	*buffer_return = CANNA_KEY_Xfer;
+      functionalChar = 1;
+      break;
+    case XK_Up:
+      if (event_struct->state & 4 /* control-shifted */)
+	*buffer_return = CANNA_KEY_Cntrl_Up;
+      else if (event_struct->state & 1 /* shifted */)
+	*buffer_return = CANNA_KEY_Shift_Up;
+      else
+	*buffer_return = CANNA_KEY_Up;
+      functionalChar = 1;
+      break;
+    case XK_Down:
+      if (event_struct->state & 4 /* control-shifted */)
+	*buffer_return = CANNA_KEY_Cntrl_Down;
+      else if (event_struct->state & 1 /* shifted */)
+	*buffer_return = CANNA_KEY_Shift_Down;
+      else
+	*buffer_return = CANNA_KEY_Down;
+      functionalChar = 1;
+      break;
+    case XK_Right:
+      if (event_struct->state & 4 /* control-shifted */)
+	*buffer_return = CANNA_KEY_Cntrl_Right;
+      else if (event_struct->state & 1 /* shifted */)
+	*buffer_return = CANNA_KEY_Shift_Right;
+      else
+	*buffer_return = CANNA_KEY_Right;
+      functionalChar = 1;
+      break;
+    case XK_Left:
+      if (event_struct->state & 4 /* control-shifted */)
+	*buffer_return = CANNA_KEY_Cntrl_Left;
+      else if (event_struct->state & 1 /* shifted */)
+	*buffer_return = CANNA_KEY_Shift_Left;
+      else
+	*buffer_return = CANNA_KEY_Left;
+      functionalChar = 1;
+      break;
+    case XK_Help:
+      *buffer_return = CANNA_KEY_Help;
+      functionalChar = 1;
+      break;
+    case XK_Home:
+      *buffer_return = CANNA_KEY_Home;
+      functionalChar = 1;
+      break;
+    }
+  return functionalChar;
+}
+
+#ifdef KC_SETLISTCALLBACK
+
+static void
+moveSelection(obj, dir)
+CannaObject obj;
+int dir;
+{
+    ICSelectionControlArg arg;
+
+    arg.command = ICSelectionMove;
+    arg.u.dir = dir;
+    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
+		       (XtPointer)&arg);
+}
+
+static int
+insertSelection(obj, selected)
+CannaObject obj;
+int selected;
+{
+    obj->canna.curcand = selected;
+    *(obj->canna.cur_addr) = selected;
+
+    return 0;
+}
+
+static void
+endSelection(obj, abort)
+CannaObject obj;
+int abort;
+{
+    ICSelectionControlArg arg;
+    int selected;
+
+    if (ignoreListfunc) return; /* SelectItem $B$G=hM}$5$l$k$N$G$3$3$OITMW(B */
+    arg.command = ICSelectionEnd;
+    arg.u.current_item = -1;
+    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
+		       (XtPointer)&arg);
+
+    if (!abort && (selected = arg.u.current_item) >= 0) {
+        insertSelection(obj, selected);
+    }
+}
+
+#ifdef CANNA_LIST_Query
+static void
+querySelection(obj)
+CannaObject obj;
+{
+  ICSelectionControlArg arg;
+  int selected;
+
+  if (ignoreListfunc) return; /* SelectItem $B$G=hM}$5$l$k$N$G$3$3$OITMW(B */
+  arg.command = ICSelectionGet;
+  arg.u.current_item = -1;
+  XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
+		     (XtPointer)&arg);
+
+  if ((selected = arg.u.current_item) >= 0) {
+    insertSelection(obj, selected);
+  }
+}
+#endif
+
+static void
+openSelection(obj, func, curitem)
+CannaObject obj;
+int func; /* $B0lHV>e$r8+$h(B */
+int curitem;
+{
+  ICSelectionControlArg arg;
+  static int current = 0;
+  static int doit = 0;
+
+  if (func == SELECTION_SET) {
+    current = curitem;
+    doit = 1;
+  }
+  else if (func == SELECTION_DO && doit) {
+    doit = 0;
+    arg.command = ICSelectionStart;
+    arg.u.selection_kind = ICSelectionCandidates;
+    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
+		       (XtPointer)&arg);
+
+    /* set current item */
+    arg.command = ICSelectionSet;
+    arg.u.current_item = current;
+    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
+		       (XtPointer)&arg);
+  }
+}
+
+static void getAllCandidates();
+
+/* listfunc -- $B8uJd0lMw%b!<%I$K$J$C$?;~$K8F$S=P$5$l$k4X?t(B
+ *
+ * obj      : KC_SETLISTCALLBACK $B$G@_Dj$7$?%/%i%$%"%s%H%G!<%?(B
+ * func     : $B8uJd0lMw5!G=$N8GM-$N5!G=$rI=$9#I#D(B
+ * items    : Start $B$N;~$K$O8uJd0lMw$NA4%"%$%F%`$,F~$k(B
+ * nitems   :      $B!7(B       $BA4%"%$%F%`$N?t$,F~$k(B
+ * cur_item :      $B!7(B       $B%+%l%s%H%"%$%F%`HV9f$r3JG<$9$k%"%I%l%9$,F~$k(B
+ */
+
+static CANNA_LISTFUNCTYPE
+listfunc(obj, func, items, nitems, cur_item)
+CannaObject obj;
+int  func;
+wchar **items;
+int nitems, *cur_item;
+{
+  ICSelectionControlArg arg;
+  int i;
+  wchar **p;
+
+  switch (func) {
+  case CANNA_LIST_Start:
+    getAllCandidates(obj, nitems, items);
+    obj->canna.numcand = nitems;
+    obj->canna.curcand = *cur_item;
+    obj->canna.cur_addr = cur_item;
+    openSelection(obj, SELECTION_SET, *cur_item);
+    break;
+  case CANNA_LIST_Select:
+    endSelection(obj, False);
+    break;
+  case CANNA_LIST_Quit:
+    endSelection(obj, True);
+    break;
+#ifdef CANNA_LIST_Query
+  case CANNA_LIST_Query:
+    querySelection(obj);
+    break;
+#endif
+#ifdef CANNA_LIST_Convert
+  case CANNA_LIST_Convert:
+#endif
+  case CANNA_LIST_Forward:
+    moveSelection(obj, ICMoveRight);
+    break;
+  case CANNA_LIST_Backward:
+    moveSelection(obj, ICMoveLeft);
+    break;
+  case CANNA_LIST_Next:
+    moveSelection(obj, ICMoveDown);
+    break;
+  case CANNA_LIST_Prev:
+    moveSelection(obj, ICMoveUp);
+    break;
+  case CANNA_LIST_BeginningOfLine:
+    moveSelection(obj, ICMoveLeftMost);
+    break;
+  case CANNA_LIST_EndOfLine:
+    moveSelection(obj, ICMoveRightMost);
+    break;
+#ifdef CANNA_LIST_Insert
+  case CANNA_LIST_Insert:
+    return 0;
+#endif
+  }
+#ifdef CANNA_LIST_Convert
+  return 1;
+#endif
+}
+
+static void
+allocCandlist(obj, n)
+CannaObject obj;
+int n;
+{
+    ICString *p;
+    int i;
+
+    if (n <= obj->canna.candlistsize) return;
+
+    if (obj->canna.candlistsize == 0) {
+	p = (ICString *)XtMalloc(n * sizeof(ICString));
+    } else {
+	p = (ICString *)XtRealloc((char *)obj->canna.candlist,
+				  n * sizeof(ICString));
+    }
+    for (i = obj->canna.candlistsize ; i < n ; i++) {
+      p[i].nbytes = p[i].nchars = (unsigned short)0;
+      p[i].data = (char *)0;
+      p[i].attr = 0;
+    }
+
+    obj->canna.candlist = p;
+    obj->canna.candlistsize = n;
+}
+
+static void
+getAllCandidates(obj, ncand, items)
+CannaObject obj;
+int ncand;
+wchar **items;
+{
+    ICString *strp;
+    wchar **p;
+    int i, bytes, chars;
+
+    allocCandlist(obj, ncand);
+
+    for (i = 0, strp = obj->canna.candlist, p = items ;
+	 i < ncand; i++, strp++, p++) {
+        int len = 0;
+
+        bytes = strp->nbytes;
+	chars = strp->nchars;
+	while ((*p)[len]) len++;
+        copyInWchar(*p, len, (wchar **)&(strp->data), &bytes, &chars);
+	strp->nbytes = bytes;
+	strp->nchars = chars;
+	strp->attr = ICAttrNormalString;
+    }
+}
+#endif /* KC_SETLISTCALLBACK */
+
+struct _keymap {
+  unsigned char cannakey;
+  KeySym keysym;
+  long modifier;
+} cannakeymap[] = {
+  {(unsigned char)'\b',		XK_BackSpace,	0},
+  {(unsigned char)'\t',		XK_Tab,		0},
+  {(unsigned char)'\n',		XK_Linefeed,	0},
+  {(unsigned char)'\013',	XK_Clear,	0},
+  {(unsigned char)'\r',		XK_Return,	0},
+  {(unsigned char)'\023',	XK_Pause,	0},
+  {(unsigned char)'\024',	XK_Scroll_Lock,	0},
+  {(unsigned char)'\e',		XK_Escape,	0},
+  {(unsigned char)CANNA_KEY_Nfer,	XK_Muhenkan,	0},
+  {(unsigned char)CANNA_KEY_Xfer,	XK_Kanji,	0},
+  {(unsigned char)CANNA_KEY_Up,		XK_Up,		0},
+  {(unsigned char)CANNA_KEY_Left,	XK_Left,	0},
+  {(unsigned char)CANNA_KEY_Right,	XK_Right,	0},
+  {(unsigned char)CANNA_KEY_Down,	XK_Down,	0},
+  {(unsigned char)CANNA_KEY_Insert,	XK_Insert,	0},
+  {(unsigned char)CANNA_KEY_Rollup,	XK_Prior,	0},
+  {(unsigned char)CANNA_KEY_Rolldown,	XK_Next,	0},
+  {(unsigned char)CANNA_KEY_Home,	XK_Home,	0},
+  {(unsigned char)CANNA_KEY_Help,	XK_Help,	0},
+  {(unsigned char)CANNA_KEY_KP_Key,	XK_KP_Space,	0}, /* ? */
+  {(unsigned char)CANNA_KEY_Shift_Nfer,	XK_Muhenkan,	ShiftMask},
+  {(unsigned char)CANNA_KEY_Shift_Xfer,	XK_Kanji,	ShiftMask},
+  {(unsigned char)CANNA_KEY_Shift_Up,	XK_Up,		ShiftMask},
+  {(unsigned char)CANNA_KEY_Shift_Left,	XK_Left,	ShiftMask},
+  {(unsigned char)CANNA_KEY_Shift_Right, XK_Right,	ShiftMask},
+  {(unsigned char)CANNA_KEY_Shift_Down,	XK_Down,	ShiftMask},
+  {(unsigned char)CANNA_KEY_Cntrl_Nfer,	XK_Muhenkan,	ControlMask},
+  {(unsigned char)CANNA_KEY_Cntrl_Xfer,	XK_Kanji,	ControlMask},
+  {(unsigned char)CANNA_KEY_Cntrl_Up,	XK_Up,		ControlMask},
+  {(unsigned char)CANNA_KEY_Cntrl_Left,	XK_Left,	ControlMask},
+  {(unsigned char)CANNA_KEY_Cntrl_Right, XK_Right,	ControlMask},
+  {(unsigned char)CANNA_KEY_Cntrl_Down,	XK_Down,	ControlMask},
+  {(unsigned char)CANNA_KEY_F1,		XK_F1,		0},
+  {(unsigned char)CANNA_KEY_F2,		XK_F2,		0},
+  {(unsigned char)CANNA_KEY_F3,		XK_F3,		0},
+  {(unsigned char)CANNA_KEY_F4,		XK_F4,		0},
+  {(unsigned char)CANNA_KEY_F5,		XK_F5,		0},
+  {(unsigned char)CANNA_KEY_F6,		XK_F6,		0},
+  {(unsigned char)CANNA_KEY_F7,		XK_F7,		0},
+  {(unsigned char)CANNA_KEY_F8,		XK_F8,		0},
+  {(unsigned char)CANNA_KEY_F9,		XK_F9,		0},
+  {(unsigned char)CANNA_KEY_F10,	XK_F10,		0},
+  {(unsigned char)CANNA_KEY_PF1,	XK_KP_F1,	0},
+  {(unsigned char)CANNA_KEY_PF2,	XK_KP_F2,	0},
+  {(unsigned char)CANNA_KEY_PF3,	XK_KP_F3,	0},
+  {(unsigned char)CANNA_KEY_PF4,	XK_KP_F4,	0},
+  {(unsigned char)CANNA_KEY_PF5,	XK_F15,		0},
+  {(unsigned char)CANNA_KEY_PF6,	XK_F16,		0},
+  {(unsigned char)CANNA_KEY_PF7,	XK_F17,		0},
+  {(unsigned char)CANNA_KEY_PF8,	XK_F18,		0},
+  {(unsigned char)CANNA_KEY_PF9,	XK_F19,		0},
+  {(unsigned char)CANNA_KEY_PF10,	XK_F20,		0},
+};
+
+#define NCANNAKEYMAP (sizeof(cannakeymap) / sizeof(struct _keymap))
+
+/* ARGSUSED */
+static int
+GetTriggerKeys(w, keys_return)
+Widget w;
+ICTriggerKey **keys_return;
+{
+  CannaObject obj = (CannaObject)w;
+  ICTriggerKey *keys;
+  unsigned char c, mkeys[256], *p, *ekeys; /* enough */
+  int n;
+  struct _keymap *pk, *ek;
+
+  n = wcKanjiControl((int)obj, KC_MODEKEYS, (char *)mkeys);
+
+  *keys_return = keys =
+    (ICTriggerKey *)XtMalloc(2 * n * sizeof(ICTriggerKey));
+  /* $B!V(B2 *$B!W$N0UL#(B:
+     $B0l$D$N%3!<%I$K(B2$B$D$N(B X $B$N%-!<$,B8:_$7$F$$$?$j$9$k;v$,$"$k!#(B
+     ($BNc(B) C-h $B"*(B Ctl<Key>h, <Key>Backspace */
+
+  if (keys) {
+    for (p = mkeys, ekeys = p + n ; p < ekeys ; p++, keys++) {
+      c = *p;
+      if (c == (unsigned char)0) {
+	keys->keysym = XK_space;
+	keys->modifiers = keys->modifiermask = ControlMask;
+	keys++;
+	keys->keysym = XK_at;
+	keys->modifiers = keys->modifiermask = ControlMask;
+      }
+      else if (c < ' ') {
+	keys->keysym = XK_quoteleft + c;
+	keys->modifiers = keys->modifiermask = ControlMask;
+	for (pk = cannakeymap, ek = cannakeymap + NCANNAKEYMAP ;
+	     pk < ek ; pk++) {
+	  if (c == pk->cannakey) {
+	    keys++;
+	    keys->keysym = pk->keysym;
+	    keys->modifiers = keys->modifiermask = pk->modifier;
+	  }
+	}
+      }
+      else if (c <= '~') {
+	keys->keysym = XK_space + (c - (unsigned char)' ');
+	keys->modifiers = keys->modifiermask = 0;
+      }
+      else if (c == 0x7f) {
+	keys->keysym = XK_Delete;
+	keys->modifiers = keys->modifiermask = 0;
+      }
+      else if ((unsigned char)0xa1 <= c && c <= (unsigned char)0xdf) {
+	keys->keysym = XK_kana_fullstop + (c - (unsigned char)0xa1);
+	keys->modifiers = keys->modifiermask = pk->modifier;
+      }
+      else {
+	keys--;
+	for (pk = cannakeymap, ek = cannakeymap + NCANNAKEYMAP ;
+	     pk < ek ; pk++) {
+	  if (c == pk->cannakey) {
+	    keys++;
+	    keys->keysym = pk->keysym;
+	    keys->modifiers = keys->modifiermask = pk->modifier;
+	  }
+	}
+      }
+    }
+    return n;
+  }
+  return 0;
+}
+
+/* ARGSUSED */
+static int
+PreeditString(w, segn, offset, encoding, format, length, string)
+Widget w;
+int segn;
+int offset;
+Atom *encoding;
+int *format;
+int *length;
+XtPointer *string;
+{
+    CannaObject obj = (CannaObject)w;
+    iBuf *ib = obj->canna.ibuf;
+    int i;
+    wchar *wbuf, *wp;
+    int len, wlen;
+    extern int convJWStoCT();
+
+    if (ib == NULL) return -1;
+    if (segn < ib->nseg && offset >= ib->len[segn]) {
+	/* $B%;%0%a%s%H$N:G8e(B */
+	++segn;
+	offset = 0;
+    }
+    if (segn >= ib->nseg || offset >= ib->len[segn]) {
+	/* $B:o=|$5$l$?(B */
+	*encoding = XA_COMPOUND_TEXT(XtDisplayOfObject(w));
+	*format = 8;
+	*length = 0;
+	*string = (XtPointer)XtMalloc(1);
+	return 0;
+    }
+
+    wlen = 0;
+    for (i = segn; i < ib->nseg; i++) {
+	wlen += ib->len[i];
+    }
+    wlen -= offset;
+
+    wp = wbuf = (wchar *)XtMalloc((wlen + 1) * sizeof(wchar));
+    len = ib->len[segn] - offset;
+    (void)bcopy((char *)(ib->str[segn] + offset), (char *)wp, sizeof(wchar) * len);
+    wp += len;
+    for (i = segn + 1; i < ib->nseg; i++) {
+	len = ib->len[i];
+	(void)bcopy((char *)ib->str[i], (char *)wp, sizeof(wchar) * len);
+	wp += len;
+    }
+    wbuf[wlen] = 0;
+
+    /*
+     * Canna $B%*%V%8%'%/%H$O(B COMPOUND_TEXT $B%(%s%3!<%G%#%s%0$7$+%5%]!<%H$7$J$$(B
+     * COMPOUND_TEXT $B$KJQ49$9$k(B
+     */
+    *encoding = XA_COMPOUND_TEXT(XtDisplayOfObject(w));
+    *format = 8;
+
+    /* COMPOUND_TEXT $B$O(B \r $B$,Aw$l$J$$$N$G(B \n $B$KJQ49$7$F$*$/(B */
+    for (wp = wbuf; *wp != 0; wp++) {
+	if (*wp == '\r') *wp = '\n';
+    }
+
+    *length = len = convJWStoCT(wbuf, (unsigned char *)NULL, 0);
+    *string = (XtPointer)XtMalloc(len + 1);
+    (void)convJWStoCT(wbuf, (unsigned char *)*string, 0);
+
+    /* wbuf $B$r(B free $B$7$F$*$/(B */
+    XtFree((char *)wbuf);
+
+    return 0;
+}
+
+/* ARGSUSED */
+static int
+StatusString(w, encoding, format, length, string, nchars)
+Widget w;
+Atom *encoding;
+int *format;
+int *length;
+XtPointer *string;
+int *nchars;
+{
+    ICString *seg;
+    wchar *wbuf, *wp;
+    int len, wlen;
+    extern int convJWStoCT();
+
+    seg = GetMode(w);
+    if (seg == NULL) {
+	*length = *nchars = 0;
+	return -1;
+    }
+
+    wlen = seg->nchars;
+    if (wlen <= 0) {
+	*length = *nchars = 0;
+	return -1;
+    }
+
+    /*
+     * data $B$KF~$C$F$$$kJQ49%F%-%9%H$O(B null $B%?!<%_%M!<%H$5$l$F$$$J$$$+$b(B
+     * $B$7$l$J$$$N$G!"$^$:%3%T!<$7$F(B null $B%?!<%_%M!<%H$9$k(B
+     */
+    wbuf = (wchar *)XtMalloc((wlen + 1) * sizeof(wchar));
+    (void)bcopy(seg->data, (char *)wbuf, sizeof(wchar) * wlen);
+    wbuf[wlen] = 0;
+
+    /*
+     * Canna $B%*%V%8%'%/%H$O(B COMPOUND_TEXT $B%(%s%3!<%G%#%s%0$7$+%5%]!<%H$7$J$$(B
+     * COMPOUND_TEXT $B$KJQ49$9$k(B
+     */
+    *encoding = XA_COMPOUND_TEXT(XtDisplayOfObject(w));
+    *format = 8;
+
+    /* COMPOUND_TEXT $B$O(B \r $B$,Aw$l$J$$$N$G(B \n $B$KJQ49$7$F$*$/(B */
+    for (wp = wbuf; *wp != 0; wp++) {
+	if (*wp == '\r') *wp = '\n';
+    }
+
+    *length = len = convJWStoCT(wbuf, (unsigned char *)NULL, 0);
+    *string = XtMalloc(len + 1);
+    (void)convJWStoCT(wbuf, (unsigned char *)*string, 0);
+    *nchars = seg->nchars;
+
+    /* wbuf $B$r(B free $B$7$F$*$/(B */
+    XtFree((char *)wbuf);
+
+    return 0;
+}