view lib/Ruby.c @ 16:598fcbe482b5

imported patch 19_kinput2-v3.1-ruby.patch
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Mon, 08 Mar 2010 20:38:17 +0900
parents
children
line wrap: on
line source

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

/* Copyright 2003 Hiroyuki Komatsu <komatsu@taiyaki.org> (Ruby.c) */

/* 直さなければならないところ

 ・Destroy が呼ばれないので CloseUIContext できない。
 ・モード領域の大きさ。(これは他のファイルだろうな)

 */

#ifndef lint
static char *rcsid = "$Id: Ruby.c,v 1.2 2003/06/10 02:11:23 komatsu 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 "RubyP.h"
#include "DebugPrint.h"

#define wchar_t wchar

#include "ruby-c.h"

static XtResource resources[] = {
#define offset(field) XtOffset(RubyObject, ruby.field)
    { XtNrubyhost, XtCRubyhost, XtRString, sizeof(String),
	offset(rubyhost), XtRString, NULL },
    { XtNrubyfile, XtCRubyfile, XtRString, sizeof(String),
	offset(rubyfile), 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 int GetTriggerKeys();
static int PreeditString();
static int StatusString();

static void convend();

#if 1 /* KC_SETLISTCALLBACK */
static void openSelection();
#define SELECTION_SET 0 /* SelectionStart をしても良いと言う情報を設定する */
#define SELECTION_DO  1 /* 実際に SelectionStart を開始する */
#else /* !KC_SETLISTCALLBACK */
#define openSelection(x, y, z)
#endif /* !KC_SETLISTCALLBACK */

static ICString *GetAuxSegments();

RubyClassRec rubyClassRec = {
  { /* object fields */
    /* superclass		*/	(WidgetClass) &inputConvClassRec,
    /* class_name		*/	"Ruby",
    /* widget_size		*/	sizeof(RubyRec),
    /* 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,
  },
  { /* ruby fields */
    /* foo			*/	0,
  }
};

WidgetClass rubyObjectClass = (WidgetClass)&rubyClassRec;

static void fix();

static ICString *SymbolList;
static int NumSymbols;

static void addObject();
static void deleteObject();

static Display *displaybell = (Display *)0;

static void rubyEUCtoICString();
static void rubyEUCArraytoICStringArray();
static void rubySelectionEvent();
static void rubySelectionDisplay();
static void rubyDialogDisplay();
static void rubyDisplayPreEdit();

typedef struct {
  ICString *array;
  int length;
} ICStringArray;

static ICString *ruby_tmp_seg; 
static ICStringArray *ruby_cand_list;

static void
ClassInitialize()
{
  ruby_init();

  ruby_tmp_seg = XtMalloc(sizeof(ICString));
  ruby_tmp_seg->nchars = 0;
  ruby_tmp_seg->nbytes = 0;
  ruby_cand_list = XtMalloc(sizeof(ICStringArray));
  ruby_cand_list->length = 0;
  
  ruby_eval("$LOAD_PATH.concat(eval(`ruby -e 'p $LOAD_PATH'`))");
  ruby_eval("$LOAD_PATH.uniq!");

  if (ruby_eval("ENV['KINPUT2_RUBY']") != Qnil) {
    ruby_eval("require ENV['KINPUT2_RUBY']");
  } else {
    ruby_eval("require 'kinput2_default'");
  }
  if (ruby_eval("global_variables.member?('$kanjiConv')") == Qfalse) {
    fprintf(stderr, "Abort: KanjiConv was not initialized.\n");
    exit (-1);
  }
  ruby_eval("kanjiConv = $kanjiConv");
  TRACE(("RubyObjectClass initialized\n"));
}

static int
InputEvent(w, event)
Widget w;
XEvent *event;
{
    RubyObject obj = (RubyObject)w;
    char key_buf[200];
    KeySym keysym;
    int len, nbytes, functionalChar;

    /* KeyPress以外は捨てる */
    if (event->type != KeyPress /*&& event->type != KeyRelease*/) return 0;

    /* 取りあえず文字に直してしまう */
    key_buf[0] = '\0';
    key_buf[1] = '\0';
    key_buf[2] = '\0';
    nbytes = XLookupString(event, key_buf, 20, &keysym, NULL);

    if (keysym == XK_space && (event->xkey.state & ShiftMask)) {
      convend(obj);
      return 0;
    }

    /* ベルを鳴らすディスプレイの設定 */
    displaybell = XtDisplayOfObject((Widget)obj);

    /* かな漢字変換する */
    {
      VALUE pass;
      printf ("key_buf: `%d'\n", key_buf[0]);
      printf ("strlen(key_buf): `%d'\n", strlen(key_buf));
      printf ("nbytes: `%d'\n", nbytes);
      pass = ruby_evalf ("kanjiConv.inputEvent(%d, %d, %d)",
			 key_buf[0], (int)keysym, (int)(event->xkey.state));
      printf ("Input: done\n");

      rubyDisplayPreEdit(obj);

      return NUM2INT(pass); /* 1: pass the key / 0: trap the key */
    }
}


static void
rubyDialogDisplay(obj)
RubyObject obj;
{
  ICAuxControlArg arg;

  /* I'm not sure about Aux. */
  if (ruby_eval("kanjiConv.dialog.visible") == Qfalse) {
    if (ruby_eval("kanjiConv.dialog.call_open") == Qtrue &&
	ruby_eval ("kanjiConv.dialog.text.length > 0") == Qtrue) {
      printf ("Dialog: open\n");
      arg.command = ICAuxStart;
      XtCallCallbackList((Widget)obj, obj->inputConv.auxcallback,
			 (XtPointer)&arg);
      ruby_eval("kanjiConv.dialog.open_end");
    }
  } else { /* value_dialog_visible == true */
    if (ruby_eval("kanjiConv.dialog.call_close") == Qtrue) {
      printf ("Dialog: close\n");
      arg.command = ICAuxEnd;
      XtCallCallbackList((Widget)obj, obj->inputConv.auxcallback,
			 (XtPointer)&arg);
      ruby_eval("kanjiConv.dialog.close_end");
      printf ("Dialog: close...done\n");
    } else {
      arg.command = ICAuxChange;
      XtCallCallbackList((Widget)obj, obj->inputConv.auxcallback,
			 (XtPointer)&arg);
    }
  }
}

static void
rubySelectionEvent(obj)
RubyObject obj;
{
  /*** Right ***/
  if (ruby_eval("kanjiConv.selection.call_right") == Qtrue) {
    moveSelection(obj, ICMoveRight);
    ruby_eval("kanjiConv.selection.right_end");
  }
    
  /*** Left ***/
  if (ruby_eval("kanjiConv.selection.call_left") == Qtrue) {
    moveSelection(obj, ICMoveLeft);
    ruby_eval("kanjiConv.selection.left_end");
  }

  /*** Down ***/
  if (ruby_eval("kanjiConv.selection.call_down") == Qtrue) {
    moveSelection(obj, ICMoveDown);
    ruby_eval("kanjiConv.selection.down_end");
  }

  /*** Up ***/
  if (ruby_eval("kanjiConv.selection.call_up") == Qtrue) {
    moveSelection(obj, ICMoveUp);
    ruby_eval("kanjiConv.selection.up_end");
  }

  /*** Beginning Line ***/
  if (ruby_eval("kanjiConv.selection.call_line_beginning") == Qtrue) {
    moveSelection(obj, ICMoveLeftMost);
    ruby_eval("kanjiConv.selection.line_beginning_end");
  }

  /*** End Line ***/
  if (ruby_eval("kanjiConv.selection.call_line_end") == Qtrue) {
    moveSelection(obj, ICMoveRightMost);
    ruby_eval("kanjiConv.selection.line_end_end");
  }

  /*** Get Index ***/
  {
    ICSelectionControlArg arg;
    arg.command = ICSelectionGet;
    arg.u.current_item = -1;

    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
		       (XtPointer)&arg);

    if (arg.u.current_item >= 0) {
      ruby_evalf ("kanjiConv.set_cand_index (%d)", arg.u.current_item);
    }
  }
}

static void
moveSelection(obj, dir)
RubyObject obj;
int dir;
{
  ICSelectionControlArg arg;

  arg.command = ICSelectionMove;
  arg.u.dir = dir;
  XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
		     (XtPointer)&arg);
}

static void
rubySelectionDisplay(obj)
RubyObject obj;
{
  ICSelectionControlArg arg;

  if (ruby_eval("kanjiConv.selection.call_open") == Qtrue) {
    printf ("Selection: open\n");
    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 = 0; /* INDEX of ITEM */
    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
		       (XtPointer)&arg);

    ruby_eval("kanjiConv.selection.open_end");
  } else if (ruby_eval("kanjiConv.selection.call_close") == Qtrue) {
    printf ("Selection: close\n");
    arg.command = ICSelectionEnd;
    arg.u.current_item = -1;
    XtCallCallbackList((Widget)obj, obj->inputConv.selectioncallback,
		       (XtPointer)&arg);
    ruby_eval("kanjiConv.selection.close_end");
  }
}


static void
rubyDisplayPreEdit(obj)
RubyObject obj;
{
    Widget w = (Widget)obj;
    
    if (ruby_eval("kanjiConv.call_fix") == Qtrue) {
      fix(obj);
    }

    /* 入力モードをチェックする */
/* #if 0 */
/*     if (ks->info & KanjiModeInfo) { */
/*       printf("modechangecallback\n"); */
    XtCallCallbackList(w, obj->inputConv.modechangecallback,
		       (XtPointer)NULL);
/*     } */
/* #endif */

    rubyDialogDisplay(obj);
    rubySelectionEvent(obj);
    rubySelectionDisplay(obj);

    XtCallCallbackList(w, obj->inputConv.textchangecallback,
		       (XtPointer)NULL);
}




static ICString *
GetMode(w)
Widget w;
{
    rubyEUCtoICString (ruby_eval ("kanjiConv.modeline"), ruby_tmp_seg);
    ruby_tmp_seg->attr = ICAttrNormalString;
    return ruby_tmp_seg;
}

static int
CursorPos(w, nsegp, ncharp)
Widget w;
Cardinal *nsegp;
Cardinal *ncharp;
{
    /*
    return value : =0 corsor ON  /    =1 OFF
    nseg: >0 segment index / =0 no segment
    nchar: number of chars; 'テs' == 2
    */
    *ncharp = NUM2INT(ruby_eval("kanjiConv.input.cursor"));
    *nsegp  = 0;

/*     printf ("CursorPos: nseg=%d, nchar=%d\n", (int)*nsegp, (int)*ncharp); */

    return (*ncharp == 0) ? 1: 0;
}

static int
NumSegments(w)
Widget w;
{
    int num;
    printf ("NumSegments\n");
    num = NUM2INT(ruby_eval("kanjiConv.segment_length"));
/*     printf ("NumSegments: num=%d\n", num); */
    return num;
}

static ICString *
GetSegment(w, n)
Widget w;
Cardinal n;
{
    RubyObject obj = (RubyObject)w;
    static ICString seg;

    printf("GetSegment\n");

    rubyEUCtoICString (ruby_evalf ("kanjiConv.segment_word(%d)", n),
		       ruby_tmp_seg);
    if (ruby_evalf("kanjiConv.segment_status(%d) == :highlight", n) == Qtrue) {
      ruby_tmp_seg->attr = ICAttrConverted | ICAttrCurrentSegment;
    } else {
      ruby_tmp_seg->attr = ICAttrNotConverted;
    }
    /* ICAttrConverted | ICAttrCurrentSegment : Inversion. */
    /* |= ICAttrChanged : Non-Underline. */
    return ruby_tmp_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;

    printf("CompareSegment\n");

    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;
{
    RubyObject obj = (RubyObject)w;

    printf("GetItemList\n");
    rubyEUCArraytoICStringArray(ruby_eval("kanjiConv.cand_list"),
				ruby_cand_list);
    *n = ruby_cand_list->length;
    return ruby_cand_list->array;
}

static int
SelectItem(w, n)
Widget w;
int n;
{

  RubyObject obj = (RubyObject)w;
  printf ("selectItem\n");
  ruby_evalf("kanjiConv.selection_fix (%d)", n);
  /* FIXME: Create new fix function in the feature. */
  fix (obj);
  XtCallCallbackList(w, obj->inputConv.textchangecallback,
		     (XtPointer)NULL);
  return 0;
}

static int
ConvertedString(w, encoding, format, length, string)
Widget w;
Atom *encoding;
int *format;
int *length;
XtPointer *string;
{
    RubyObject obj = (RubyObject)w;
    wchar *wbuf, *wp;
    int len, wlen;
    extern int convJWStoCT();

    /* メモリの解放は自動的に行われる. str を直接書き換えてはいけない. */
    unsigned char *str = STR2CSTR(ruby_eval("kanjiConv.value_fixed"));

    printf("ConvertedString\n");

    wbuf = XtMalloc((strlen(str) + 1) * sizeof(wchar));
    euc2wcs(str, strlen(str), wbuf);
    wlen = wstrlen(wbuf);

    /*
     * Ruby オブジェクトは COMPOUND_TEXT エンコーディングしかサポートしない
     * COMPOUND_TEXT に変換する
     */
    *encoding = XA_COMPOUND_TEXT(XtDisplayOfObject((Widget)obj));
    *format = 8;

    /* COMPOUND_TEXT は \r が送れないので \n に変換しておく */
    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);

    XtFree(wbuf);

    return 0;
}

static int
ClearConversion(w)
Widget w;
{
  ruby_eval ("kanjiConv.clear");
  return 0;
}

static ICString *
GetAuxSegments(w, n, ns, nc)
Widget w;
Cardinal *n, *ns, *nc;
{
  /* I'm not sure about this function's purpose. */
  /* n  => ngseg ; number of segments (1 <= n <= 3). */
  /* ns => nseg  ; index of reversed segment (0 or 1). */
  /* nc => nchar ; length of current segment??? */ 
  printf ("GetAuxSegments\n");

  if ((ruby_eval ("kanjiConv.dialog.visible") == Qtrue ||
       ruby_eval ("kanjiConv.dialog.call_open") == Qtrue) &&
      ruby_eval ("kanjiConv.dialog.text.length > 0") == Qtrue) {
    if (n) {
      *n = 1;
    }

    if (ns) {
      *ns = 0;
    }

    rubyEUCtoICString (ruby_eval ("kanjiConv.dialog.text"),
		       ruby_tmp_seg);
    ruby_tmp_seg->attr = ICAttrConverted;

    if (nc) {
      *nc = ruby_tmp_seg->nchars;
    }

    return ruby_tmp_seg;
  } else {
    if (n) {
      *n = 0;
    }
    if (ns) {
      *ns = 0;
    }
    if (nc) {
      *nc = 0;
    }
    return NULL;
  }
}

/* ARGSUSED */
static void
Initialize(req, new, args, num_args)
Widget req;
Widget new;
ArgList args;
Cardinal *num_args;
{
    RubyObject obj = (RubyObject)new;

    obj->ruby.selectionending = False;
    obj->ruby.textchanged = False;
    obj->ruby.symbollist = SymbolList;
    obj->ruby.numsymbols = NumSymbols;
    obj->ruby.cursymbol = 0;
    obj->ruby.candlist = NULL;
    obj->ruby.candlistsize = 0;
    obj->ruby.numcand = 0;
    obj->ruby.lastTextLengthIsZero = False;
      
    addObject(obj);
}

static int
bell()
{
  if (displaybell) {
    XBell(displaybell, 0);
  }
  return 0;
}

static int nRubyContexts = 0;

static void
Destroy(w)
Widget w;
{
    RubyObject obj = (RubyObject)w;
    wchar buf[512];
    int i;
    
    if (obj->ruby.candlist) {
      for (i = 0 ; i < obj->ruby.candlistsize ; i++) {
	if ((obj->ruby.candlist + i)->data) {
	  XtFree((obj->ruby.candlist + i)->data);
	}
      }

      XtFree((char *)obj->ruby.candlist);
    }

    /* Ruby 用終了処理をここに書く */

    deleteObject(obj);
}

static Boolean
SetValues(cur, req, wid, args, num_args)
Widget cur;
Widget req;
Widget wid;
ArgList args;
Cardinal *num_args;
/* ARGSUSED */
{
    RubyObject old = (RubyObject)cur;
    RubyObject new = (RubyObject)wid;

    return False;	     
}

static void
fix(obj)
RubyObject obj;
{
    /* 確定の処理 */
    XtCallCallbackList((Widget)obj, obj->inputConv.fixcallback,
		       (XtPointer)NULL);
    ruby_eval("kanjiConv.fix_end");
}

static void
convend(obj)
RubyObject obj;
{
    XtCallCallbackList((Widget)obj, obj->inputConv.endcallback,
		       (XtPointer)NULL);
    ruby_eval("kanjiConv.reset");
}

/*
 * keeping list of objects
 */
typedef struct _oblist_ {
    RubyObject obj;
    struct _oblist_ *next;
} ObjRec;

static ObjRec *ObjList = NULL;

static void
addObject(obj)
RubyObject obj;
{
    ObjRec *objp = XtNew(ObjRec);

    objp->obj = obj;
    objp->next = ObjList;
    ObjList = objp;
}

static void
deleteObject(obj)
RubyObject 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;
	}
    }
}

/* ARGSUSED */
static int
GetTriggerKeys(w, keys_return)
Widget w;
ICTriggerKey **keys_return;
{
  /* 何をする関数なのか不明 (komatsu) */

  *keys_return = NULL;
  return 0;
}

/* ARGSUSED */


/*
 * int ICGetPreeditString(Widget object, int segn, int offset,
 *                        Atom *encoding, int *format,
 *                        int *length, XtPointer *string)
 *      変換途中の segn 番目の セグメントの offset 文字からのテキストを
 *      string に返す
 *      encoding には、テキストのエンコーディングを指定しておく
 *      ただしこれは単なるリクエストであって、変換オブジェクトは
 *      別のエンコーディングで返してもよい
 *      encoding には実際のエンコーディングが返される
 *      変換オブジェクトは少なくとも COMPOUND_TEXT エンコーディングは
 *      サポートしなくてはならない
 *      format には 8/16/32 のいずれか、length は string のエレメント数が
 *      それぞれ返される
 *      テキストの領域は malloc されているのでこの関数を呼んだ側で
 *      free しなければならない
 *      変換テキストがない時やエラーの場合には -1、そうでなければ 0 が
 *      関数の値として返される
 */
/* まだ, 未検討 (komatsu) */
static int
PreeditString(w, segn, offset, encoding, format, length, string)
Widget w;
int segn;
int offset;
Atom *encoding;
int *format;
int *length;
XtPointer *string;
{
    RubyObject obj = (RubyObject)w;

    return -1;
}

/* 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();

    printf("StatusString\n");

    seg = GetMode(w);
    if (seg == NULL) {
	*length = *nchars = 0;
	return -1;
    }

    wlen = seg->nchars;
    if (wlen <= 0) {
	*length = *nchars = 0;
	return -1;
    }

    /*
     * data に入っている変換テキストは null ターミネートされていないかも
     * しれないので、まずコピーして null ターミネートする
     */
    wbuf = (wchar *)XtMalloc((wlen + 1) * sizeof(wchar));
    (void)bcopy(seg->data, (char *)wbuf, sizeof(wchar) * wlen);
    wbuf[wlen] = 0;

    /*
     * Ruby オブジェクトは COMPOUND_TEXT エンコーディングしかサポートしない
     * COMPOUND_TEXT に変換する
     */
    *encoding = XA_COMPOUND_TEXT(XtDisplayOfObject(w));
    *format = 8;

    /* COMPOUND_TEXT は \r が送れないので \n に変換しておく */
    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 を free しておく */
    XtFree((char *)wbuf);

    return 0;
}

/***
ic_str->nbytes が 0 の場合 ic_str->data は自動的に確保 (XtMalloc) される.
 0 以外の場合 ic_str->data は (XtRealloc) される.
 */
static void
rubyEUCtoICString(ruby_str, ic_str)
VALUE ruby_str;
ICString *ic_str;
{
  unsigned char *euc_str;
  int euc_len;
  wchar *wc_str;
  
/*   printf("String_Len: %d\n", RSTRING(ruby_str)->len); */

  euc_str = STR2CSTR(ruby_str);
  euc_len = strlen(euc_str);

  if (ic_str->nbytes == 0) {
    wc_str = (wchar *)XtMalloc((euc_len + 1) * sizeof(wchar));
  } else {
    wc_str = (wchar *)XtRealloc(ic_str->data, (euc_len + 1) * sizeof(wchar));
  }
  euc2wcs(euc_str, euc_len, wc_str);

  ic_str->data   = (char *)wc_str;
  ic_str->nchars = wstrlen(wc_str);
  ic_str->nbytes = (euc_len + 1) * sizeof(wchar);
  ic_str->attr   = ICAttrNotConverted;
}

static void
rubyEUCArraytoICStringArray(ruby_str_ary, ic_str_ary)
VALUE ruby_str_ary;
ICStringArray *ic_str_ary;
{
  VALUE ruby_str;
  int i, array_length;
  ICString ic_str;

  array_length = RARRAY(ruby_str_ary)->len;
  if ((ic_str_ary->length) < array_length) {
    ic_str_ary->array = (ICString *)XtRealloc((char *)ic_str_ary->array,
					      array_length * sizeof(ICString));
    for (i = ic_str_ary->length; i < array_length; i++) {
/*       ic_str_ary->array[i] = (ICString *)XtMalloc(1 * sizeof(ICString)); */
      ic_str_ary->array[i].nbytes = 0;
    }
  }
  ic_str_ary->length = array_length;

/*   printf("array_length: %d\n", array_length); */

  for (i = 0; i < array_length; i++) {
    rubyEUCtoICString(rb_ary_entry(ruby_str_ary, i), &(ic_str_ary->array[i]));
  }
}