/* # skkinput (Simple Kana-Kanji Input)
 * cstyle.c --- common routines with root/over-the-spot/off-the-spot.
 * This file is part of skkinput.
 * Copyright (C) 1997
 * Takashi SAKAMOTO (sakamoto@yajima.kuis.kyoto-u.ac.jp)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with skkinput; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>

#include "commondef.h"
#include "resrcs.h"
#include "buffers.h"
#include "skkbuf.h"
#include "config.h"
#include "skkkey.h"
#include "skkel.h"
#include "MyError.h"

/* 
 * プロトタイプ宣言。
 */
static int do_function_by_reading_character
( Widget gw, struct skkinputBuffer *buffer,
  struct SKKInputNode *node ) ;
static int skkinput_do_function
( Widget gw, struct skkinputBuffer *buffer, struct SKKInputNode *node,
  int textflag, int *ret_func_no ) ;
static int do_skkinputFunction_withtext
( Widget gw, struct skkinputBuffer *buffer,
  struct SKKInputNode *node, int func_no ) ;
static int do_skkinputFunction_withouttext
( Widget gw, struct skkinputBuffer *buffer, 
  struct SKKInputNode *node, int func_no ) ;

/* keymap.c */
extern int lookup_at_keymap
( struct keyBuffer *keybuf, struct SKKInputNode *node ) ;
extern void skkinput_initKeyBuffer
( struct keyBuffer *keybuf ) ;
extern void skkinput_closeKeyBuffer
( struct keyBuffer *keybuf ) ;
extern void skkinput_addKeyBuffer
( struct keyBuffer *keybuf, int chara ) ;

enum {
  SIW_DESTROYED = -1, SIW_PROCESSING = 0,
} ;

/*
 * グローバル変数宣言。
 */
extern Boolean skkinput_autosave_key_dirty ;

/*
 * skkinput のバッファの初期化を行う関数。
 */
void skkinput_bufferInit( struct skkinputBuffer *buffer )
{
  struct SKKInputNode *node ;

  /* cutbuffer の初期化をする。*/
  MYCHAR_SET_END_OF_STRING( buffer->cutbuffer[ 0 ] ) ;
  /* キー入力の情報を初期化しておく。*/
  skkinput_initKeyBuffer( &buffer->keybuf ) ;
  /* 編集用のバッファの確保。*/
  node = skkinput_AllocateMinibuffer() ;
  node->cur_exist = True ;
  buffer->topbuffer = buffer->lastbuffer = node ;
  /* ヒストリの初期化をする。*/
  buffer->hist_start = buffer->hist_end = 0 ;
  buffer->hist_cur = -1 ;
  buffer->historybuffer = buffer->histcurbackbuffer = NULL ;
  /* 取り敢えずは無効化しておく。*/

  /* buffer->toggleShiftMode   = False ;
   * buffer->toggleControlMode = False ; */
  buffer->currentShiftStatus   = False ;
  buffer->currentControlStatus = False ;

  buffer->redraw = NULL ;
  return ;
}

#ifndef XK_Page_Up
#define XK_Page_Up XK_Prior
#endif
#ifndef XK_Page_Down
#define XK_Page_Down	XK_Next
#endif

/*
 * XKeyEvent から入力された文字を判断する関数。
 *----
 * 基本的に XLookupString の結果を利用している…これを使っている限りに
 * おいて任意の KeySym に対して機能を割り当てることは困難なのだが…仕
 * 方ないかな？
 */
static int skkinputLookupString
( struct skkinputBuffer *buffer )
{
  static KeySym misckeys[] = {
    XK_Home,	XK_Left,	XK_Up,		XK_Right,
    XK_Down,	XK_Page_Up,	XK_Page_Down,	XK_End,
    XK_Begin,	XK_Insert,	XK_Clear,	XK_Help,
    XK_Break,	XK_Henkan_Mode,			XK_Muhenkan,
    XK_F1,	XK_F2,		XK_F3,		XK_F4,
    XK_F5,	XK_F6,		XK_F7,		XK_F8,
    XK_F9,	XK_F10,		XK_F11,		XK_F12,
    XK_VoidSymbol,
  } ;
  XKeyEvent *xkev = &( buffer->xevent.xkey ) ;
  int shiftStatus, controlStatus, chara, i ;
  char inpstr[ STRBUFSIZE ] ;
  KeySym key, *kptr ;

  shiftStatus   = buffer->currentShiftStatus ;
  controlStatus = buffer->currentControlStatus ;
  buffer->currentShiftStatus = buffer->currentControlStatus = False ;

  if( buffer->toggleShiftMode   && shiftStatus )
    xkev->state |= ShiftMask ;
  if( buffer->toggleControlMode && controlStatus )
    xkev->state |= ControlMask ;

  /* 文字列受信バッファの初期化。*/
  inpstr[ 0 ] = '\0' ;
  /* 文字列を受信。*/
  XLookupString( xkev, inpstr, STRBUFSIZE, &key, NULL ) ;
  if( ( chara = inpstr[ 0 ] ) == '\0' ){
    switch( key ){
    case XK_space :
      if( xkev->state & ControlMask )
	chara = CHARA_CONTROL_SPACE ;
      break ;
    case XK_Shift_L :
    case XK_Shift_R :
      buffer->currentShiftStatus = ( shiftStatus )? False : True ;
      break ;
    case XK_Control_L :
    case XK_Control_R :
      buffer->currentControlStatus = ( controlStatus )? False : True ;
      break ;
    default :
      i    = 0 ;
      kptr = misckeys ;
      while( *kptr != XK_VoidSymbol ){
	if( key == *kptr ){
	  chara = CHARA_HOME + ( i << 1 ) + 
	    ( ( xkev->state & ControlMask )? 1 : 0 ) ;
	  break ;
	}
	kptr ++ ;
	i ++ ;
      }
      break ;
    }
  } else {
    if( chara == 0x20 && ( xkev->state & ShiftMask ) &&
	!( xkev->state & ( ~ShiftMask ) ) ){
      chara = CHARA_SHIFT_SPACE ;
    }
  }
  return chara ;
}

/*
 * 共通のキーイベント処理関数。
 *----
 * キーイベントの処理方法については、Root/OverTheSpot/OffTheSpot のい
 * ずれも同じように処理可能である。
 */
int commonKeyEventHandler
( Widget gw, XEvent *xevent, struct skkinputBuffer *buffer )
{
  struct SKKInputNode *node, *topbuffer ;
  int chara ;

  /* キーイベントが来たので自動セーブはこの期間の最後に実行することは
   * 無いとする。*/
  skkinput_autosave_key_dirty = True ;

  /* キーが押されたのでなければ抜ける。*/
  if( xevent->type != KeyPress )
    return False ;

  /* イベントを記憶しておく。*/
  buffer->xevent = *xevent ;

  /* ミニバッファが存在しているのならば、その一番最後のものを選択する。*/
  topbuffer = buffer->topbuffer ;
  node      = buffer->lastbuffer ;
  if( !node->cur_exist )
    node = topbuffer ;

  if( ( chara = skkinputLookupString( buffer ) ) != '\0' ){
    if( chara == CHARA_SHIFT_SPACE ){
      XtDestroyWidget( gw ) ;
      return False ;
    } else if( chara == CHARA_CONTROL_SPACE ){
      chara = 0 ;
    }
    skkinput_addKeyBuffer( &buffer->keybuf, chara ) ;
    /* ミニバッファが存在し、そこが利用されている場合の処理。*/
    switch( do_function_by_reading_character( gw, buffer, node ) ){
    case SIW_DESTROYED :
      XtDestroyWidget( gw ) ;
      return False ;
    case SIW_PROCESSING :
      break ;
    default :
      break ;
    }
  } else {
    /* skkinput では認識できないが意味があるかもしれないキーを送
     * り返す必要がある。*/ 
    if( IS_END_OF_STRING( topbuffer->textbuffer[ 0 ] ) &&
	topbuffer->minibuffer == NULL && 
	buffer->overthespot_like_input )
      j_sendback_keypress( gw, &( buffer->xevent ) ) ;
  }
  return True;
}

/*
 * 入力された文字列からそれに対応する機能を実行する関数。
 */
static int do_function_by_reading_character
( Widget gw, struct skkinputBuffer *buffer, struct SKKInputNode *node )
{
  struct SKKInputNode *topbuffer ;
  struct keyBuffer    *keybuf ;
  int textflag, rest_pos, func_no, keycode, prev_kmode, ret ;

  topbuffer = buffer->topbuffer ;
  keybuf    = &( buffer->keybuf ) ;
  ret       = SIW_PROCESSING ;

  /* キーが一文字も入っていない状況では動く筈もない。*/
  if( keybuf->usage <= 0 )
    return ret ;
  /* 既にウィンドウが作られているかどうかを判断する。変換動作中なら、 *
   * その中での動作は skkinput が担当しなければならないが、それ以外な *
   * らほとんどのキーは送り返すことになるので。*/
  prev_kmode = topbuffer->j_kana_mode ;
  if( !IS_END_OF_STRING( topbuffer->textbuffer[ 0 ] ) ||
      topbuffer->minibuffer != NULL ||
      !buffer->overthespot_like_input ){
      textflag = True ;
  } else {
    textflag = False ;
  }
  keycode = keybuf->buffer[ keybuf->usage - 1 ] ;

  ret = skkinput_do_function( gw, buffer, node, textflag, &func_no ) ;

  /* 入力された文字を変換クライアントへと送るべきかどうかを判断する。*
   * over-the-spot like の入力方式を入れていなければ続く処理はもちろ *
   * ん不要になる。 */
  if( !topbuffer->cur_exist ||
      topbuffer->minibuffer != NULL ||
      !IS_END_OF_STRING( topbuffer->mtextbuffer[ 0 ] ) ||
      !buffer->overthespot_like_input ||
      buffer->xevent.xany.window == None )
    return ret ;
  /* 仮に topbuffer が空になっていたとして、仮名モードはどうなってま * 
   * すか？ */
  if( IS_END_OF_STRING( topbuffer->textbuffer[ 0 ] ) ){
    if( topbuffer->j_kana_mode ||
	!prev_kmode || 
	func_no <= FUNCNO_J_SET_MARK_COMMAND ||
	func_no == FUNCNO_J_KEYBOARD_QUIT ||
	func_no == FUNCNO_J_DELETE_BACKWARD_CHAR )
      return ret ;
    /* キーを送ってみる。*/
    j_sendback_keypress( gw, &( buffer->xevent ) ) ;
    buffer->xevent.xany.window = None ;
    return ret ;
  } else if( func_no == FUNCNO_SELF_INSERT_CHARACTER && !textflag &&
	     IS_ASCII_EQUAL( topbuffer->textbuffer[ 0 ], keycode ) ){
    /* XLookup String した結果の文字と入っている文字が同じ場合…。*
     * キーを送ってみる。*/
    j_sendback_keypress( gw, &( buffer->xevent ) ) ;
    j_delete_region( topbuffer, 0, 1 ) ;
    return ret ;
  }
  /* 仮名モードであったり、変換中であったりした場合には、入力中のテキ
     スト全てを送信してしまうわけにはいかない。行頭から何処まで送信し
     てよいかは、それぞれのスタート位置に縛られている。*/
  if( topbuffer->j_kana_mode ||
      topbuffer->j_henkan_mode ||
      topbuffer->j_henkan_on ||
      topbuffer->j_okurigana_mode ){
    rest_pos = topbuffer->cur_pos ;
    /* 仮名モードであったら、仮名開始位置に縛られる。*/
    if( topbuffer->j_kana_mode &&
	topbuffer->j_kana_start_point < rest_pos )
      rest_pos = topbuffer->j_kana_start_point ;
    /* 変換中ならば変換開始位置と変換終了位置に縛られる。*/
    if( topbuffer->j_henkan_mode ||
	topbuffer->j_henkan_on ){
      int tmp_rest_pos = rest_pos + 1 ;
      if( topbuffer->j_henkan_start_point < tmp_rest_pos &&
	  topbuffer->j_henkan_start_point >= 0 )
	tmp_rest_pos = topbuffer->j_henkan_start_point ;
      if( topbuffer->j_henkan_end_point < tmp_rest_pos && 
	  topbuffer->j_henkan_end_point >= 0 )
	tmp_rest_pos = topbuffer->j_henkan_end_point ;
      rest_pos = tmp_rest_pos - 1 ;
    }
    /* 送り仮名モードならば送り仮名開始位置に縛られる。*/
    if( topbuffer->j_okurigana_mode &&
	topbuffer->j_okurigana_start_point < rest_pos )
      rest_pos = topbuffer->j_okurigana_start_point ;
  } else {
    /* そうでなければ全てが送信対象になる。*/
    rest_pos = myCharStrlen( topbuffer->textbuffer ) ;
  }
#ifdef DEBUG
  /* デバッグのためにそれぞれのマーカの位置と送信終了位置を表示する。*/
  fprintf
    ( stderr, "Send: cur_pos(%d), kana(%d), henkan(%d)-(%d), okuri(%d), rest_pos(%d)\n",
      node->cur_pos, topbuffer->j_kana_start_point, 
      topbuffer->j_henkan_start_point, topbuffer->j_henkan_end_point,
      topbuffer->j_okurigana_start_point, rest_pos ) ;
#endif
  /* 送るべき文字列が何も存在しなかったら以下の処理は不要になる。*/
  if( rest_pos <= 0 )
    return ret ;
  /* 文字列を送信する。*/
  myCharStrncpy( topbuffer->mtextbuffer, topbuffer->textbuffer, rest_pos ) ;
  MYCHAR_SET_END_OF_STRING( topbuffer->mtextbuffer[ rest_pos ] ) ;
#ifdef DEBUG
  fprintf( stderr, "(j-delete-region 0 %d)\n", rest_pos ) ;
  fprintf( stderr, "(text)\"" ) ;
  myCharFputstring( stderr, topbuffer->textbuffer ) ;
  fprintf( stderr, "\"\n" ) ;
#endif
  j_delete_region( topbuffer, 0, rest_pos ) ;
#ifdef DEBUG
  fprintf( stderr, "After (j-delete-region 0 %d)\n", rest_pos ) ;
  fprintf( stderr, "(text)\"" ) ;
  myCharFputstring( stderr, topbuffer->textbuffer ) ;
  fprintf( stderr, "\"\n" ) ;
#endif
  /* 変換クライアントへ現在入力されている文字列を送信する。*/
  XtCallCallbacks
    ( gw, XtNfixNotify, ( XtPointer )topbuffer->mtextbuffer ) ;
  MYCHAR_SET_END_OF_STRING( topbuffer->mtextbuffer[ 0 ] ) ;

  /* 先の入力によって、仮名モードが確定してしまっているのなら…。*/
  if( !prev_kmode || !IS_END_OF_STRING( topbuffer->textbuffer[ 0 ] ) ||
      topbuffer->j_kana_mode || topbuffer->j_henkan_mode )
    return ret ;
  /* 入力系 j-insert ほげほげ等の場合には、実行されない。*/
  if( func_no <= FUNCNO_J_SET_MARK_COMMAND ||
      func_no == FUNCNO_J_KEYBOARD_QUIT ||
      func_no == FUNCNO_J_DELETE_BACKWARD_CHAR )
    return ret ;
  /* キーを送ってみる。*/
  j_sendback_keypress( gw, &( buffer->xevent ) ) ;
  buffer->xevent.xany.window = None ;
  return ret ;
}

/*
 * キー一つの場合の処理を行う関数。
 */
static int skkinput_do_function
( Widget gw,
  struct skkinputBuffer *buffer, struct SKKInputNode *node,
  int textflag, int *ret_func_no )
{
  struct keyBuffer *keybuf ;
  int func_no ;

  keybuf  = &buffer->keybuf ;
  func_no = FUNCNO_NOFUNCTION ;

  /* 変換処理中の候補選択モードなのでせうか？ */
  if( node->j_henkan_show_candidate_mode ){
    /* 以前は不良なキーだったらはねる処理とかあったけど…まあ、このモー*
     * ドががんばってくれるから大丈夫かな？ */
    do_Function_jhenkanShowCandidateMode( gw, buffer, node ) ;
    goto do_function_by_onekey_exit ;
  } else if( node->j_input_by_code_or_menu_mode ){
    if( node->j_input_by_code_or_menu_mode == True ){
      do_function_j_input_by_code_or_menu_jump( gw, buffer, node ) ;
    } else {
      do_function_j_input_by_code_or_menu_1_jump( gw, buffer, node ) ;
    }
    goto do_function_by_onekey_exit ;
  }
  /* 入力された最初の文字を見て…機能がなんであったかを取り出す。*/
  func_no = lookup_at_keymap( keybuf, node ) ;
  /* 
   * 多分、この段階で skk-rom-kana-rule-list をチェックしないといけな
   * い。そうでなければコントロールコードでもそれが使えるということに
   * はならないから。
   */
  switch( func_no ){
  case FUNCNO_J_INSERT_A :
  case FUNCNO_J_INSERT_E :
  case FUNCNO_J_INSERT_I :
  case FUNCNO_J_INSERT_O :
  case FUNCNO_J_INSERT_U :
  case FUNCNO_J_KANA_INPUT :
  case FUNCNO_J_SET_HENKAN_POINT :
    break ;
  default :
    /* 仮名入力の途中だったりした場合には、もしかすると   *
     * rom-kana-rule-list にヒットしたりするかもしれない。*/
    if( node->j_kana_mode ){
      if( rom_kana_rule_list_check
	  ( node, keybuf->buffer[ keybuf->usage - 1 ] ) ){
	if( node->j_henkan_mode && !node->j_henkan_on &&
	    node->j_okurigana_mode )
	  j_set_okurigana
	    ( gw, buffer, node, node->j_okuri_chara ) ;
	/* この場合例えもとは j-newline でも実行されないのだから無視。*/
	func_no = FUNCNO_NOFUNCTION ;
	goto do_function_by_onekey_exit ;
      }
    }
    /* ここに oh のチェックが入る。*/
    j_oh_check_with_controlcode
      ( node, ( ( func_no == FUNCNO_J_KEYBOARD_QUIT )? True : False ) ) ;
    break ;
  }
  *ret_func_no = func_no ;
  /* 与えられた機能を実行致します。*/
  if( textflag ){
    return do_skkinputFunction_withtext( gw, buffer, node, func_no ) ;
  } else {
    return do_skkinputFunction_withouttext( gw, buffer, node, func_no ) ;
  }
do_function_by_onekey_exit:
  *ret_func_no = func_no ;
  skkinput_closeKeyBuffer( keybuf ) ;
  return SIW_PROCESSING ;
}

/*
 * キーテーブルを参照した結果のファンクション番号に従って処理を実行す
 * べく他の関数を呼出す処理を行う関数。
 *-----
 * skkinput の中で文字列を編集する必要があるため、幾つかのコントロール
 * を skkinput が奪うようになっている。もし、Root モードなら、常にこの
 * モードになる。OverTheSpot モードの場合には、このモードに入るのは変
 * 換動作中だけである。
 */
static int do_skkinputFunction_withtext
( Widget gw, struct skkinputBuffer *buffer,
  struct SKKInputNode *node, int func_no )
{
  struct keyBuffer *keybuf ;
  int chara ;

  keybuf = &buffer->keybuf ;
  chara  = keybuf->buffer[ keybuf->usage - 1 ] ;

  /* 何かキーが押されると、下のメッセージバッファに書かれていたもの *
   * は消されることになっている？ */
  MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;

  /* 補完計画を終了させるかどうか。*/
  if( node->j_completion_mode ){
    if( func_no != FUNCNO_J_INSERT_PERIOD &&
	func_no != FUNCNO_J_INSERT_COMMA  &&
	func_no != FUNCNO_J_ABBREV_PERIOD &&
	func_no != FUNCNO_J_ABBREV_COMMA  &&
	func_no != FUNCNO_J_TRY_COMPLETION ){
      j_completion_close( node ) ;
    }
  }
  /* 与えられた命令に対する処理を行う。*/
  switch( func_no ){
  case FUNCNO_J_SENDBACK_KEY :
    j_sendback_keypress( gw, &( buffer->xevent ) ) ;
    buffer->xevent.xany.window = None ;
    break ;
  case FUNCNO_J_SENDBACK_NEXT_KEY :
    j_delete_kanaprefix( node ) ;
    return SIW_PROCESSING ;
  case FUNCNO_J_PREFIX_CHAR :
    j_delete_kanaprefix( node ) ;
    /* 一番↑のバッファならば、C- とか表示する。*/
    if( node == buffer->topbuffer ){
      myCharCharStrcpy( node->mtextbuffer, charaToString( chara ) ) ;
      myCharCharStrcat( node->mtextbuffer, "-" ) ;
    }
    return SIW_PROCESSING ;
  case FUNCNO_SELF_INSERT_CHARACTER :
    j_insert_chara( node, node->cur_pos, chara ) ;
    break ;
  case FUNCNO_J_SELF_INSERT :
    j_self_insert
      ( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_SELF_ZENKAKU_INSERT :
    j_insert_zenkaku( node, node->cur_pos, chara ) ;
    break ;
  case FUNCNO_J_DISPLAY_CODE_FOR_CHAR_AT_POINT :
    j_display_code_for_char_at_point( node ) ;
    break ;
  case FUNCNO_J_SET_HENKAN_POINT :
    /* 変換開始位置が設定される。*/
    j_set_henkan_point( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_SET_HENKAN_POINT_SUBR :
    j_set_henkan_point_subr( gw, buffer, node, chara ) ;
    break ;
    /* 母音の入力。*/
  case FUNCNO_J_INSERT_A :
    j_insert_a( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_E :
    j_insert_e( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_I :
    j_insert_i( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_O :
    j_insert_o( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_U :
    j_insert_u( gw, buffer, node, chara ) ;
    break ;
    /* 仮名入力の開始キー( 全部子音である )。*/
  case FUNCNO_J_KANA_INPUT :
    j_kana_input( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_START_HENKAN :
    j_start_henkan( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_INSERT_COMMA :
    j_insert_comma( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_INSERT_PERIOD :
    j_insert_period( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_PURGE_FROM_JISYO :
    j_purge_from_jisyo( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_INPUT_BY_CODE_OR_MENU :
    j_input_by_code_or_menu_start( gw, buffer, node ) ;
    break ;
  case FUNCNO_VC_TOGGLE_OVERTHESPOT_LIKE_INPUT :
    /* overthespot like な入力にするかどうかを交換する。*/
    buffer->overthespot_like_input =
      ( buffer->overthespot_like_input )? False : True ;
    break ;
  case FUNCNO_J_MODE_OFF :
    j_mode_off( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_MODE_OFF_AND_SELF_INSERT :
    j_mode_off_and_self_insert( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_TOGGLE_KANA :
    /* 仮名モードを片仮名モードの切り替え。*/
    j_toggle_kana( gw, buffer, node, False, chara ) ;
    break ;
  case FUNCNO_J_PREVIOUS_CANDIDATE :
    j_previous_candidate( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_KAKUTEI :
    if( j_kakutei_kana_mode( gw, buffer, node, chara ) )
      break ;
    j_kakutei( gw, node ) ;
    break ;
  case FUNCNO_J_ABBREV_INPUT :
    j_abbrev_input( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_ABBREV_PERIOD :
    j_abbrev_period( gw, node ) ;
    break ;
  case FUNCNO_J_ABBREV_COMMA :
    j_abbrev_comma( gw, node ) ;
    break ;
  case FUNCNO_J_ZENKAKU_EIJI :
    /* 全角(変な日本語)英字の入力モードにする。*/
    j_zenkaku_eiji( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_ZENKAKU_HENKAN :
    j_zenkaku_henkan( gw, node ) ;
    break ;
  case FUNCNO_J_TODAY :
    /* 日付の出力。*/
    j_today( node ) ;
    break ;
  case FUNCNO_J_KANAINPUT_MODE_ON :
    j_kanainput_mode_on( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_SET_MARK_COMMAND :
    j_set_mark_command( node ) ;
    break ;
  case FUNCNO_J_NEWLINE :
    j_newline( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_FORWARD_CHAR :
    j_forward_char( node ) ;
    break ;
  case FUNCNO_J_BACKWARD_CHAR :
    j_backward_char( node ) ;
    break ;
  case FUNCNO_J_DELETE_CHAR :
    j_delete_char( node ) ;
    break ;
  case FUNCNO_J_DELETE_BACKWARD_CHAR :
    j_backward_delete_char( gw, buffer, node, True ) ;
    break ;
  case FUNCNO_J_TRY_COMPLETION :
    j_try_completion( gw, node ) ;
    break ;
  case FUNCNO_J_END_OF_LINE :
    j_end_of_line( node ) ;
    break ;
  case FUNCNO_J_BEGINNING_OF_LINE :
    j_beginning_of_line( node ) ;
    break ;
  case FUNCNO_J_KILL_LINE :
    j_kill_line( node, buffer->cutbuffer ) ;
    break ;
  case FUNCNO_J_KILL_REGION :
    j_kill_region( node, buffer->cutbuffer ) ;
    break ;
  case FUNCNO_J_KILL_RING_SAVE :
    j_kill_ring_save( node, buffer->cutbuffer ) ;
    break ;
  case FUNCNO_J_EXCHANGE_POINT_AND_MARK :
    j_exchange_point_and_mark( node ) ;
    break ;
  case FUNCNO_J_YANK :
    j_yank( node, buffer->cutbuffer ) ;
    break ;
  case FUNCNO_J_PREVIOUS_LINE :
    j_previous_line( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_NEXT_LINE :
    j_next_line( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_TRANSPOSE_CHARS :
    j_transpose_chars( node ) ;
    break ;
  case FUNCNO_J_REDRAW :
    /* 一旦フル再描画する。差分ではなくて。*/
    buffer->redraw( gw ) ;
    break ;
  case FUNCNO_J_KEYBOARD_QUIT :
    j_keyboard_quit( gw, buffer, node ) ;
    break ;
  case FUNCNO_SAVE_SKKINPUT_LOCAL_JISYO :
    j_delete_kanaprefix( node ) ;
    /* 辞書が更新されているかどうかを判定する。*/
    if( buffer->jisyo_dirty ){
#if 1
      myCharCharStrcpy( node->mtextbuffer, "Saving Jisyo..." ) ;
      buffer->redraw( gw ) ;
      XSafeFlush( XtDisplay( gw ) ) ;
#endif
      /* 辞書の更新をするコールバックを呼出す。*/
      skkinput_updateLocalJisyo() ;
      myCharCharStrcpy( node->mtextbuffer, "Saving Jisyo...done" ) ;
    } else {
      err_msg( node, "(No changes need to be saved)" ) ;
    }
    break ;
  case FUNCNO_CLOSE_SKKINPUT :
    return SIW_DESTROYED ;
  case FUNCNO_VC_TOGGLE_CHATMODE :
    j_delete_kanaprefix( node ) ;
    buffer->chat_adapter = ( buffer->chat_adapter )? False : True ;
    break ;
  case FUNCNO_VC_TOGGLE_EGGNL :
    j_delete_kanaprefix( node ) ;
    buffer->egg_like_newline =
      ( buffer->egg_like_newline )? False : True ;
    break ;
  case FUNCNO_NOT_MODIFIED :
    j_delete_kanaprefix( node ) ;
    /* 辞書の変更フラグを消去する。*/
    j_clear_modified_flag() ;
    myCharCharStrcpy( node->mtextbuffer, "Modification-flag cleared" ) ;
    break ;
  case FUNCNO_J_FORWARD_WORD :
    j_forward_word( node ) ;
    break ;
  case FUNCNO_J_BACKWARD_WORD :
    j_backward_word( node ) ;
    break ;
  case FUNCNO_J_UPCASE_WORD :
    j_caseword( node, 'u' ) ;
    break ;
  case FUNCNO_J_DOWNCASE_WORD :
    j_caseword( node, 'd' ) ;
    break ;
  case FUNCNO_J_CAPITALIZE_WORD :
    j_caseword( node, 'c' ) ;
    break ;
  case FUNCNO_INVALID_CHAR :
  default :
#if 0
    XBell( XtDisplay( gw ), 2 ) ;
#endif
    break ;
  }
  skkinput_closeKeyBuffer( keybuf ) ;
  return SIW_PROCESSING ;
}

/*
 * キーテーブルを参照した結果のファンクション番号に従って処理を実行す
 * べく他の関数を呼出す処理を行う関数。
 *----
 * Root モードでは決して呼ばれることの無い関数である。この関数は 
 * OverTheSpot モードの時に編集中の文字列が存在しない時に呼ばれる。こ
 * の時、どの程度までのキーを skkinput が利用するのか…ということを問
 * 題とする。
 */
static int do_skkinputFunction_withouttext
( Widget gw, struct skkinputBuffer *buffer, 
  struct SKKInputNode *node, int func_no )
{
  struct keyBuffer *keybuf ;
  int chara ;

  keybuf = &buffer->keybuf ;
  chara  = keybuf->buffer[ keybuf->usage - 1 ] ;

  /* 何かキーが押されると、下のメッセージバッファに書かれていたもの *
   * は消されることになっている？ */
  MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;

  /* ここに来たら必ず completion は終了することになる。*/
  if( node->j_completion_mode ){
    j_completion_close( node ) ;
  }
  /* 与えられた命令に対する処理を行う。文字列の入力操作以外は無視する。*/
  switch( func_no ){
  case FUNCNO_SELF_INSERT_CHARACTER :
    j_insert_chara( node, node->cur_pos, chara ) ;
    break ;
  case FUNCNO_J_SELF_INSERT :
    j_self_insert( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_SELF_ZENKAKU_INSERT :
    j_insert_zenkaku( node, node->cur_pos, chara ) ;
    break ;
  case FUNCNO_J_SET_HENKAN_POINT :
    /* 変換開始位置が設定される。*/
    j_set_henkan_point( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_SET_HENKAN_POINT_SUBR :
    j_set_henkan_point_subr( gw, buffer, node, chara ) ;
    break ;
    /* 母音の入力。*/
  case FUNCNO_J_INSERT_A :
    j_insert_a( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_E :
    j_insert_e( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_I :
    j_insert_i( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_O :
    j_insert_o( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_INSERT_U :
    j_insert_u( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_PURGE_FROM_JISYO :
    break ;
    /* 仮名入力の開始キー( 全部子音である )。*/
  case FUNCNO_J_KANA_INPUT :
    j_kana_input
      ( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_START_HENKAN :
    j_start_henkan( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_INSERT_COMMA :
    j_insert_comma( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_INSERT_PERIOD :
    j_insert_period( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_INPUT_BY_CODE_OR_MENU :
    j_input_by_code_or_menu_start( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_MODE_OFF :
    j_mode_off( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_MODE_OFF_AND_SELF_INSERT :
    j_mode_off_and_self_insert( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_TOGGLE_KANA :
    /* 仮名モードを片仮名モードの切り替え。*/
    j_toggle_kana( gw, buffer, node, False, chara ) ;
    break ;
  case FUNCNO_J_PREVIOUS_CANDIDATE :
    j_previous_candidate( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_KAKUTEI :
    if( j_kakutei_kana_mode( gw, buffer, node, chara ) )
      break ;
    j_kakutei( gw, node ) ;
    break ;
  case FUNCNO_J_ABBREV_INPUT :
    j_abbrev_input( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_ABBREV_PERIOD :
    j_abbrev_period( gw, node ) ;
    break ;
  case FUNCNO_J_ABBREV_COMMA :
    j_abbrev_comma( gw, node ) ;
    break ;
  case FUNCNO_J_ZENKAKU_EIJI :
    /* 英字の入力モードにする。*/
    j_zenkaku_eiji( gw, buffer, node, chara ) ;
    break ;
  case FUNCNO_J_TODAY :
    /* 日付の出力。*/
    j_today( node ) ;
    break ;
  case FUNCNO_J_KANAINPUT_MODE_ON :
    j_kanainput_mode_on( gw, buffer, node ) ;
    break ;
  case FUNCNO_J_NEWLINE :
    j_newline( gw, buffer, node ) ;
    break ;
  case FUNCNO_CLOSE_SKKINPUT :
    return SIW_DESTROYED ;
  case FUNCNO_VC_TOGGLE_EGGNL :
    j_delete_kanaprefix( node ) ;
    buffer->egg_like_newline =
      ( buffer->egg_like_newline )? False : True ;
    break ;
  case FUNCNO_VC_TOGGLE_OVERTHESPOT_LIKE_INPUT :
    /* overthespot like な入力にするかどうかを交換する。*/
    buffer->overthespot_like_input =
      ( buffer->overthespot_like_input )? False : True ;
    break ;
  case FUNCNO_SAVE_SKKINPUT_LOCAL_JISYO :
    j_delete_kanaprefix( node ) ;
    /* 辞書が更新されているかどうかを判定する。*/
    if( buffer->jisyo_dirty ){
      myCharCharStrcpy( node->mtextbuffer, "Saving Jisyo..." ) ;
      buffer->redraw( gw ) ;
      XSafeFlush( XtDisplay( gw ) ) ;
      /* 辞書の更新をするコールバックを呼出す。*/
      skkinput_updateLocalJisyo() ;
      myCharCharStrcpy( node->mtextbuffer, "Saving Jisyo...done" ) ;
    } else {
      err_msg( node, "(No changes need to be saved)" ) ;
    }
    break ;
    /* prefix 文字だった場合の処理。*/
  case FUNCNO_J_PREFIX_CHAR :
    /* この場合キーは送り返すものの…。*/
    j_sendback_keypress( gw, &( buffer->xevent ) ) ;
    buffer->xevent.xany.window = None ;
    break ;
  case FUNCNO_J_SENDBACK_KEY :
  case FUNCNO_J_SENDBACK_NEXT_KEY :
  case FUNCNO_INVALID_CHAR :
  default :
    /* ここはキーを送り返すことになる。*/
    j_sendback_keypress( gw, &( buffer->xevent ) ) ;
    buffer->xevent.xany.window = None ;
    break ;
  }
  /* キーバッファをクリアする。*/
  skkinput_closeKeyBuffer( keybuf ) ;
  return SIW_PROCESSING ;
}

/*
 * 強制的に \C-g (Quit) の機能を実行させてしまう関数。
 */
void force_keyboard_quit
( Widget gw, struct skkinputBuffer *buffer )
{
  struct SKKInputNode *node ;

  node = ( buffer->lastbuffer->cur_exist )?
    buffer->lastbuffer : buffer->topbuffer ;

  /* もし何か入力された文字がバッファにたまっていたら、それは抹消され
     る。*/
  skkinput_closeKeyBuffer( &buffer->keybuf ) ;
  skkinput_addKeyBuffer( &buffer->keybuf, 0x07 ) ;

  if( node->j_henkan_show_candidate_mode ){
    do_Function_jhenkanShowCandidateMode( gw, buffer, node ) ;
  } else if( node->j_input_by_code_or_menu_mode ){
    if( node->j_input_by_code_or_menu_mode == True ){
      do_function_j_input_by_code_or_menu_jump( gw, buffer, node ) ;
    } else {
      do_function_j_input_by_code_or_menu_1_jump( gw, buffer, node ) ;
    }
  } else if( buffer->topbuffer != buffer->lastbuffer ){
    j_keyboard_quit( gw, buffer, node ) ;
  } else {
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
  }
  skkinput_closeKeyBuffer( &buffer->keybuf ) ;
  /* 再描画させる。*/
  buffer->redraw( gw ) ;
  return ;
}

