/* # skkinput (Simple Kana-Kanji Input)
 * SeparateWin.c --- Making SeparateWidget.
 * 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/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>

#include "SeparateWinP.h"
#include "config.h"
#include "skkkey.h"
#include "skkel.h"
#include "Canvas.h"
#include "WmcloseShell.h"
#include "MyError.h"
#include "FontMgr.h"
#include "draw.h"
#include "MyDispatch.h"

#ifndef XtNgeometry
#define XtNgeometry		"geometry"
#define XtCGeometry		"Geometry"
#endif

#define offset(field) XtOffsetOf( SeparateWinRec, separateWin.field)
#define goffset(field) XtOffsetOf( WidgetRec, core.field )

static XtResource resources[] = {
  /* もとからあるリソース。*/
  { XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
    goffset(width), XtRImmediate, (XtPointer) 640},
  { XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
    goffset(height), XtRImmediate, (XtPointer) 3},
  { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel),
    goffset(background_pixel), XtRString, XtDefaultBackground },
  /* 自分とこで作ったリソース。*/
  { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
    offset(puppixel), XtRString, XtDefaultForeground },
  { XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean),
    offset(reverse_video), XtRImmediate, (XtPointer) FALSE},
  /* 利用する漢字フォント及びローマ字フォント。*/
  { XtNfontset, XtCFontset,  XtRString, sizeof( String ),
    offset(fontset_string), XtRImmediate, (XtPointer)DEFAULT_FONTSET },
  /* skkinput 窓が閉じる時に利用される callback */
  { XtNendNotify, XtCCallback, XtRCallback, sizeof(caddr_t), 
    offset(endcallback), XtRCallback, (caddr_t)NULL },
  /* skkinput から client に文字列を送信する時に利用される callback */
  { XtNfixNotify, XtCCallback, XtRCallback, sizeof(caddr_t), 
    offset(fixcallback), XtRCallback, (caddr_t)NULL },
  /* key event を client に送り返す時に利用される callback */
  { XtNkeybackNotify, XtCCallback, XtRCallback, sizeof(caddr_t), 
    offset(keybackcallback), XtRCallback, (caddr_t)NULL },
  { XtNtextNotify,	XtCCallback, XtRCallback, sizeof (XtCallbackList),
	offset (m_lstTextCallback),	XtRCallback, (caddr_t) NULL },
  /* Shift-Space で skkinput 窓を閉じれるようにするか否か。*/
  { XtNcompatibleCloseSkkinputKey, XtCCompatibleCloseSkkinputKey,
    XtRBoolean, sizeof ( Boolean ),
    offset(compatible_close_skkinputkey), XtRImmediate, (XtPointer)TRUE },
  /* egg 互換の j-newline を許すか否か。*/
  { XtNeggLikeNewline, XtCEggLikeNewline, XtRImmediate, sizeof( Boolean ),
    XtOffsetOf( SeparateWinRec, separateWin.buffer.egg_like_newline ),
    XtRImmediate, (XtPointer) FALSE},
  /* chat-adapter-mode か否か。*/
  { XtNchatAdapter, XtCChatAdapter, XtRImmediate, sizeof( Boolean ),
    XtOffsetOf( SeparateWinRec, separateWin.buffer.chat_adapter ),
    XtRImmediate, (XtPointer) FALSE},
  { XtNjisyoDirty, XtCJisyoDirty, XtRImmediate, sizeof( Boolean ),
    XtOffsetOf( SeparateWinRec, separateWin.buffer.jisyo_dirty ),
    XtRImmediate, (XtPointer) FALSE },
  { XtNclearMinibuffer, XtCClearMinibuffer, XtRImmediate, sizeof( Boolean ),
    offset( clearMinibuffer ), XtRImmediate, (XtPointer)FALSE },
  /* カーソルを常に南側に表示する…。*/
  { XtNsouthCursor, XtCSouthCursor, XtRBoolean, sizeof( Boolean ),
    offset( south_cursor ), XtRImmediate, ( XtPointer )FALSE },
  /* history の初期化に用いる関数。*/
  { XtNconversionHistory, XtCConversionHistory, XtRImmediate,
    sizeof( HistoryListNode * ), offset( historyAttribute ),
    XtRImmediate, ( XtPointer )NULL },
  /* クライアントウィンドウ。*/
  { XtNclientWindow, XtCClientWindow, XtRImmediate, sizeof( Window ),
    offset( client_window ), XtRImmediate, (XtPointer)None },
  { XtNsetFocus, XtCSetFocus, XtRImmediate, sizeof( int ),
    offset( conversion_set_focus ), XtRImmediate, (XtPointer)FALSE },
  { XtNunsetFocus, XtCUnsetFocus, XtRImmediate, sizeof( int ),
    offset( conversion_unset_focus ), XtRImmediate, (XtPointer)FALSE },
  { XtNconversionAttribute, XtCConversionAttribute, XtRImmediate, 
    sizeof( struct ConvAttrsMesg * ),
    offset( camsg ), XtRImmediate, ( XtPointer )NULL },
  { XtNoverTheSpotLikeInput, XtCOverTheSpotLikeInput, XtRBoolean,
    sizeof( Boolean ), offset( overthespotLikeInput ),
    XtRImmediate, ( XtPointer )FALSE },
  { XtNshiftHaTugiDeYukou, XtCShiftHaTugiDeYukou, XtRBoolean,
    sizeof( Boolean ),
    XtOffsetOf( SeparateWinRec, separateWin.buffer.toggleShiftMode ),
    XtRImmediate, ( XtPointer )FALSE },
  { XtNcontrolHaTugiDeYukou, XtCControlHaTugiDeYukou, XtRBoolean,
    sizeof( Boolean ), 
    XtOffsetOf( SeparateWinRec, separateWin.buffer.toggleControlMode ),
    XtRImmediate, ( XtPointer )FALSE },
  {	XtNinput,	XtCInput,	XtRBoolean,	sizeof (Boolean),
	offset (m_fInputFocus), XtRImmediate, (XtPointer) TRUE, },
} ;

#undef offset
#undef goffset

/*
 * プロトタイプ宣言。
 */
/*
 * このウィジェットを構成している基本要素となる関数たち。
 */
static void SEPW_Initialize
( Widget greq, Widget gnew, ArgList args, Cardinal *num_args ) ;
static void SEPW_Realize
( Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs ) ;
static void SEPW_Redisplay
( Widget gw, XEvent *event, Region region ) ;
static void SEPW_Destroy( Widget gw ) ;
static Boolean SEPW_SetValues
( Widget current, Widget request, Widget new,
  ArgList args, Cardinal *num_args ) ;
static void KeyDownEventHandler
( Widget gw, XEvent *event, String *params, Cardinal *num_params ) ;
static void FocusEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params ) ;
static int SEPW_ChangeAttributes
( Widget gw, unsigned long valuemask, struct ConvAttrs *values ) ;

static void SEPW_RedrawScreen( Widget gw ) ;
static void SEPW_DrawScreen( Widget gw ) ;
static void SEPW_ClearMinibuffer( Widget gw ) ;

static void SeparateCanvas_RedisplayCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void Canvas_DestroyCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void WMMessageCloseCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void WMShellDestroyCallback
( Widget gw, caddr_t client, caddr_t caller ) ;
static void		SeparateWin_fixcallback		(Widget, XtPointer, XtPointer) ;

static void skkinput_FullDrawModeLine
( Widget gw, struct SKKInputNode *node, 
  int x, int y, unsigned int width ) ;
static void skkinput_DiffDrawModeLine
( Widget gw, struct SKKInputNode *node,
  int x, int y, unsigned int width ) ;

/* cstyle.c */
extern void skkinput_bufferInit( struct skkinputBuffer *buffer ) ;
extern int commonKeyEventHandler
( Widget gw, XEvent *xevent, struct skkinputBuffer *buffer ) ;

/*
 * グローバル変数。
 */
static XtActionsRec separateWinActionsTable [] = {
  { "KeyDownEventHandler",	   KeyDownEventHandler },
  { "FocusInEventHandler",	   FocusEventHandler },
  { "FocusOutEventHandler",	   FocusEventHandler },
} ;

static char defaultSeparateWinTranslations[] =  
"<Key>:                   KeyDownEventHandler()\n\
 <FocusIn>:               FocusInEventHandler()\n\
 <FocusOut>:              FocusOutEventHandler()\n" ;

SeparateWinClassRec separateWinClassRec = {
    { /* core fields */
    /* superclass		*/	&widgetClassRec,
    /* class_name		*/	"Separate",
    /* size			*/	sizeof(SeparateWinRec),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	SEPW_Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	SEPW_Realize,
    /* actions			*/	separateWinActionsTable,
    /* num_actions		*/	XtNumber( separateWinActionsTable ),
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber( resources ),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	SEPW_Destroy,
    /* resize			*/	NULL,
    /* expose			*/	SEPW_Redisplay,
    /* set_values		*/	SEPW_SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	defaultSeparateWinTranslations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    }
};

WidgetClass separateWinWidgetClass = ( WidgetClass )&separateWinClassRec ;

/*
 * Root Window Style の描画に必要な GC を全て作成する関数。
 */
static void SEPW_CreateAllGCs( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  XGCValues	myXGCV ;
  myXGCV.foreground = w->separateWin.puppixel ;
  myXGCV.background = w->core.background_pixel ;
  w->separateWin.gc = XCreateGC
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      GCForeground | GCBackground, &myXGCV ) ;
  myXGCV.foreground = w->core.background_pixel ;
  myXGCV.background = w->separateWin.puppixel ;
  w->separateWin.rgc = XCreateGC
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      GCForeground | GCBackground, &myXGCV ) ;
  return ;
}

static void SEPW_InitializeHistory( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;

  if( w->separateWin.historyAttribute == NULL ){
    return ;
  }
  /* 以前に設定したことがあれば…。*/
  if( w->separateWin.historyAttribute->overthespot_like_input < 0 ){
    /* X Resource の側から設定する。*/
    w->separateWin.buffer.overthespot_like_input = 
      w->separateWin.overthespotLikeInput ;
  } else {
    /* 前回の設定を流用する。*/
    w->separateWin.buffer.overthespot_like_input = 
      w->separateWin.historyAttribute->overthespot_like_input ;
  }
  /* ヒストリの初期化をする。*/
  w->separateWin.buffer.historybuffer =
    w->separateWin.historyAttribute->history ;
  w->separateWin.buffer.hist_start = 
    w->separateWin.historyAttribute->history_start ;
  w->separateWin.buffer.hist_end = 
    w->separateWin.historyAttribute->history_end ;
  w->separateWin.buffer.histcurbackbuffer =
    w->separateWin.historyAttribute->histcurbak ;
  w->separateWin.buffer.hist_cur = -1 ;
  /* 入力モードの初期化をする。*/
  w->separateWin.buffer.chat_adapter =
    w->separateWin.historyAttribute->chat_adapter ;
  w->separateWin.buffer.egg_like_newline =
    w->separateWin.historyAttribute->egg_nl ;
  /* 仮名モードの初期化をする。*/
  w->separateWin.buffer.topbuffer->j_mode =
    w->separateWin.historyAttribute->j_mode ;
  w->separateWin.buffer.topbuffer->j_zenkaku =
    w->separateWin.historyAttribute->j_zenkaku ;
  w->separateWin.buffer.topbuffer->j_katakana_mode =
    w->separateWin.historyAttribute->j_katakana_mode ;
  return ;
}

static void SEPW_Initialize 
( Widget greq, Widget gnew, ArgList args, Cardinal *num_args )
{
  SeparateWinWidget w = ( SeparateWinWidget )gnew ;
  int i ;

  for( i = 0 ; i < NUMBER_OF_CHARSET ; i ++ )
    w->separateWin.fontset[ i ] = NULL ;
  w->separateWin.gc = w->separateWin.rgc = NULL ;

  /* こっちはバッファ毎の制御なんて考えてないが…軽い。筈…。*/
  fontMgr_copyDefaultFontSet
    ( XtDisplay( gnew ), w->separateWin.fontset ) ;
  /* その他の初期化。*/
  MYCHAR_SET_END_OF_STRING( w->separateWin.pwrite_string[ 0 ] ) ;
  MYCHAR_SET_END_OF_STRING( w->separateWin.pmtextbuffer[ 0 ] ) ;
  /* バッファ及びヒストリの初期化を行う。*/
  skkinput_bufferInit( &( w->separateWin.buffer ) ) ;
  /* 再描画を行う関数を指定しておく。*/
  w->separateWin.buffer.redraw = SEPW_RedrawScreen ;
  /* まあ、Focus は最初は外れていると思うよ。*/
  w->separateWin.is_focus = False ;
  w->separateWin.canvas_probe = False ;
  w->separateWin.focus_window = None ;
  /* カーソルの大きさの設定。*/
  w->separateWin.cursor_width = 8 ;
  w->separateWin.prev_modeline = ( unsigned long )-1L ;
  /* ヒストリの初期化。*/
  SEPW_InitializeHistory( gnew ) ;
  return ;
}

static void SEPW_CanvasPopupPositionInit
( Widget gw, Window client, 
  int canvas_width, int canvas_height, int canvas_border )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  Window win ;
  int x, y, pos_x, pos_y ;
  unsigned int client_width, client_height, border_width, depth ;
  unsigned int root_width, root_height ;
  long supplied ;
  XSizeHints sizehints ;
  
  XGetGeometry
    ( XtDisplay( gw ), client, &win, &x, &y,
      &client_width, &client_height,
      &border_width, &depth ) ;
  XGetGeometry
    ( XtDisplay( gw ), RootWindowOfScreen( XtScreen( gw ) ),
      &win, &x, &y, &root_width, &root_height, &border_width, &depth ) ;
  XTranslateCoordinates
    ( XtDisplay( gw ), client, RootWindowOfScreen( XtScreen( gw ) ),
      0, 0, &x, &y, &win ) ;
  pos_x = x ;
  pos_y = y + client_height ;
  if( ( pos_y + canvas_height ) > root_height ){
    pos_y = y - canvas_height - canvas_border ;
    if( pos_y < 0 ){
      pos_y = root_height - canvas_height - canvas_border ;
    }
    /* Window Manager にヒントを送っている。ただし、XSetWMNormalHints * 
     * は部分書換えしないようなので、以前の hints を読みとってから差し*
     * 替えるという手法をとらなければならない。*/
    XGetWMNormalHints
      ( XtDisplay( gw ), XtWindow( w->separateWin.canvas_popup ),
 	&sizehints, &supplied ) ;
    sizehints.flags |= PWinGravity ;
    sizehints.win_gravity = SouthWestGravity ;
    /* Window Manager にヒントを送っている。*/
    XSetWMNormalHints
      ( XtDisplay( gw ), XtWindow( w->separateWin.canvas_popup ),
 	&sizehints ) ;
  }
  if( pos_x + canvas_width > root_width ){
    pos_x = root_width - canvas_width - canvas_border ;
  }
  if( pos_x < 0 )
    pos_x = 0 ;
  XtMoveWidget( w->separateWin.canvas_popup, pos_x, pos_y ) ;
  return ;
}

static void
SEPW_CreateCanvasPopup (
	register Widget		gw)
{
	SeparateWinWidget w = ( SeparateWinWidget )gw ;
	unsigned int winwidth, winheight ; 
	XSizeHints sizehints ;
	Widget canvas_popup, canvas ;
	Arg arg[ 10 ] ;
	Cardinal i ;

	winwidth  = w->core.width  ;
	winheight = ( w->core.height * w->separateWin.font_height ) ;
	
	i = 0 ;
	XtSetArg( arg[ i ], XtNinput, w->separateWin.m_fInputFocus) ;
	i ++ ;
	XtSetArg( arg[ i ], XtNmappedWhenManaged, TRUE ) ;
	i ++ ;
	XtSetArg( arg[ i ], XtNwidth, winwidth ) ;
	i ++ ;
	XtSetArg( arg[ i ], XtNheight, winheight ) ;
	i ++ ;
	/* まず、ポップアップシェルを作成する。*/
	canvas_popup = XtCreatePopupShell ("skkinput", wmcloseShellWidgetClass, gw, arg, i) ;
	XtAddCallback (canvas_popup, XtNwmcloseNotify, (XtCallbackProc) WMMessageCloseCallback, gw) ;
	XtAddCallback (canvas_popup, XtNdestroyNotify, (XtCallbackProc) WMShellDestroyCallback, gw) ;
	/* その下につく表示用の widget を作成する。以前、shellwidget に直 *
	 * 接書いてはまったことがあるので…。*/
	canvas = XtVaCreateManagedWidget ("canvas", canvasWidgetClass, canvas_popup, NULL) ;
	XtAddCallback (canvas, XtNredrawNotify,  (XtCallbackProc)SeparateCanvas_RedisplayCallback, gw) ;
	XtAddCallback (canvas, XtNdestroyNotify, (XtCallbackProc )Canvas_DestroyCallback, gw) ;
	/* realize される前に代入しておく。*/
	w->separateWin.canvas_popup = canvas_popup ;
	w->separateWin.canvas = canvas ;
	XtRealizeWidget( canvas_popup ) ;
	XtRealizeWidget( canvas ) ;

	canvas_popup->core.border_width = 0 ;
	/* ここの決め打ちを何とか修正できるようにした方が良いとは思うよ。*/
	winwidth  += canvas_popup->core.border_width * 2 ;
	winheight += canvas_popup->core.border_width * 2 ;

	/* 親にリサイズ送っている？ */
	(void) XtMakeResizeRequest
		( ( Widget )canvas_popup,
		  ( Dimension )winwidth, ( Dimension )winheight,
		  &w->core.width, &w->core.height ) ;
	
	/* ウィンドウのリサイズその他の設定。*/
	sizehints.win_gravity = NorthWestGravity ;
	sizehints.base_width  = 0 ; /*canvas_popup->core.border_width * 2 ;*/
	sizehints.base_height = 0 ; /*canvas_popup->core.border_width * 2 ;*/
	sizehints.width      = winwidth ;
	sizehints.height     = winheight ;
	sizehints.width_inc  = 1 ;
	sizehints.height_inc = w->separateWin.font_height ;
	sizehints.min_width  = sizehints.base_width ;
	sizehints.min_height = w->separateWin.font_height * 3 +
		sizehints.base_height ;
	sizehints.flags      = 
		( PBaseSize | PMinSize | PResizeInc | USSize | PWinGravity ) ;
	
	/* Window Manager にヒントを送っている。*/
	XSetWMNormalHints (XtDisplay (gw), XtWindow (canvas_popup), &sizehints) ;

  /* ウィンドウの表示位置を計算する。*/
	SEPW_CanvasPopupPositionInit
		( gw, w->separateWin.focus_window,
		  winwidth, winheight, canvas_popup->core.border_width ) ;
  return ;
}

/*
 * Widget が実体を持つ時に呼ばれる関数。
 *------
 * create しただけでは実体を持たない。create した後に realize すること
 * によって実体を持つ。その時に呼ばれる関数。
 */
static void SEPW_Realize
( Widget gw, XtValueMask *valueMask, XSetWindowAttributes *attrs )
{
	SeparateWinWidget	w = ( SeparateWinWidget )gw ;
	CoreWidgetClass		super = ( CoreWidgetClass )XtClass( gw )->core_class.superclass ;

	/* 先に適切なウィンドウを用意させておく。*/
	( *super->core_class.realize )( gw, valueMask, attrs ) ;

	/* フォーカスウィンドウが設定されていなければ問題がある。*/
	if( w->separateWin.focus_window == None ){
		if( w->separateWin.client_window == None ){
			XtDestroyWidget( gw ) ;
			return ;
		}
		w->separateWin.focus_window = w->separateWin.client_window ;
	}
	/* フォントの設定が間違っていたら、終了する。(何も表示出来ないから) */
	if( !fontMgr_GetFontSetInfo
		( w->separateWin.fontset,
		  &w->separateWin.font_height,
		  &w->separateWin.font_ascent ) ){
		XtDestroyWidget( gw ) ;
		return ;
	}
	if (XtHasCallbacks (gw, XtNfixNotify) != XtCallbackHasSome) 
		XtAddCallback (gw, XtNfixNotify, SeparateWin_fixcallback, NULL) ;
	
	/* Popup とその下のキャンバスを作成する。*/
	SEPW_CreateCanvasPopup (gw) ;
	SEPW_CreateAllGCs (gw) ;
	
	/* ウィンドウを起こす。*/
	XtPopup (w->separateWin.canvas_popup, XtGrabNone) ;
	w->separateWin.canvas_probe = True ;
	/* X Input Method でなければ、フォーカスを奪う。*/
	if (w->separateWin.m_fInputFocus) {
		XtSetKeyboardFocus (w->separateWin.canvas_popup, gw) ;
		XtSetKeyboardFocus (w->separateWin.canvas, gw) ;
	}
	/* 初期画面の作成。*/
	XtVaSetValues (w->separateWin.canvas, XtNredrawRequest, True, NULL) ;
	return ;
}


static void SEPW_ReleaseAllGCs( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  if( w->separateWin.gc != NULL )
    XFreeGC( XtDisplay( gw ), w->separateWin.gc ) ;
  if( w->separateWin.rgc != NULL )
    XFreeGC( XtDisplay( gw ), w->separateWin.rgc ) ;
  w->separateWin.gc = w->separateWin.rgc = NULL ;
  return ;
}

static void SEPW_FreeFontSet( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  int i ;

  for( i = 0 ; i < NUMBER_OF_CHARSET ; i ++ ){
    fontMgr_FreeFont
      ( XtDisplay( gw ), w->separateWin.fontset[ i ] ) ;
    w->separateWin.fontset[ i ] = NULL ;
  }
  return ;
}

/*
 * SIW に XtDestroyWidget をかけた時に呼び出される関数。
 *-----
 * SIW の終了処理一揃えが入っている。GC を解放してから、malloc してい
 * たメモリを解放している。
 */
static void SEPW_Destroy( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  struct SKKInputNode *node, *pNode ;
  /*
   * XFreeFont( XtDisplay( gw ), w->separateWin.fs_kanji ) ;
   * XFreeFont( XtDisplay( gw ), w->separateWin.fs_roman ) ;
   * どうやら、Widget 毎に確保しているわけじゃないみたいなので、ここで
   * 解放するとはまるみたいだ。
   */
  /* Event を全く受けない状態にしておいてから…。*/
  XSafeSelectInput( XtDisplay( gw ), XtWindow( gw ), NoEventMask ) ;
  /* XtDestroyWidget( w->separateWin.canvas_popup ) ;
     XtDestroyWidget( w->separateWin.canvas ) ; */
  /* Event Queue をフラッシュしておく。*/
  XSafeFlush( XtDisplay( gw ) ) ;
  /* GC の解放。*/
  SEPW_ReleaseAllGCs( gw ) ;
  SEPW_FreeFontSet( gw ) ;
  /*
   * ヒストリを親へと返す。
   */
  if( w->separateWin.historyAttribute != NULL ){
    /* ヒストリについて親に返します。*/
    w->separateWin.historyAttribute->history_start =
      w->separateWin.buffer.hist_start ;
    w->separateWin.historyAttribute->history_end =
      w->separateWin.buffer.hist_end ;
    /* まあ…その仮名モードの状況を親へと返します。*/
    if( w->separateWin.buffer.topbuffer != NULL ){
      w->separateWin.historyAttribute->j_mode =
	w->separateWin.buffer.topbuffer->j_mode ;
      w->separateWin.historyAttribute->j_zenkaku =
	w->separateWin.buffer.topbuffer->j_zenkaku ;
      w->separateWin.historyAttribute->j_katakana_mode =
	w->separateWin.buffer.topbuffer->j_katakana_mode ;
    }
    /* その場変換モードだったかどうかも残しておく。*/
    w->separateWin.historyAttribute->overthespot_like_input =
      w->separateWin.buffer.overthespot_like_input ;
  }
  /*
   * バッファを全て解放しておく。
   */
  node = w->separateWin.buffer.lastbuffer ;
  while( node != NULL ){
    pNode = node->parentbuffer ;
    free_Minibuffer( gw, &w->separateWin.buffer, node ) ;
    node  = pNode ;
  }
  XtCallCallbacks( gw, XtNendNotify, w->separateWin.historyAttribute ) ;
  w->separateWin.historyAttribute = NULL ;
  return ;
}

/*
 * 画面を再描画する関数。
 */
static void SEPW_Redisplay( Widget gw, XEvent *event, Region region )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
#if 0
  /* これは何かのタイミングで間違った Widget の再描画が送られている。*/
  if( w->separateWin.topbuffer == NULL || w->core.being_destroyed ){
#if 1
    fprintf( stderr, "Illegal Widget Exposure Event.\n" ) ;
#endif
    return ;
  }
#endif
  XtVaSetValues
    ( w->separateWin.canvas, XtNredrawRequest, True, NULL ) ;
  return ;
}

static Boolean SEPW_SetValues
( Widget current, Widget request, Widget new,
  ArgList args, Cardinal *num_args )
{
  SeparateWinWidget curw = ( SeparateWinWidget )current ;
  SeparateWinWidget reqw = ( SeparateWinWidget )request ;
  SeparateWinWidget neww = ( SeparateWinWidget )new ;
  struct ConvAttrsMesg *camsg ;

  /* 変換属性の修正要求が来た場合と見付けたり。*/
  if( reqw->separateWin.camsg != NULL ){
    int ret ;
    /* 変換属性を修正する…本当にこれで言葉足りるのかどうかは不明。 *
     * やってみるっきゃないね！                                     *
     * 若葉：「そうですね。やってみましょう、…さん！」             */
    camsg = reqw->separateWin.camsg ;
    reqw->separateWin.camsg = neww->separateWin.camsg = 
      curw->separateWin.camsg = NULL ;
    /* フォーカスウィンドウの修正だけを見ている。*/
    ret = SEPW_ChangeAttributes
      ( new, camsg->mask, &( camsg->value ) ) ;
    return FALSE ;
  }

  /* キャンバスが生成されていなければ、以下の処理は無効である。*/
  if( !neww->separateWin.canvas_probe )
    return FALSE ;

  /* 辞書が修正されていたら、書き直しを実行する。*/
  if( curw->separateWin.buffer.jisyo_dirty !=
      reqw->separateWin.buffer.jisyo_dirty ){
    neww->separateWin.buffer.jisyo_dirty =
      reqw->separateWin.buffer.jisyo_dirty ;
    XtVaSetValues
      ( neww->separateWin.canvas, XtNredrawRequest, True, NULL ) ;
    return FALSE ;
  }
  if( reqw->separateWin.clearMinibuffer ){
    reqw->separateWin.clearMinibuffer = False ;
    SEPW_ClearMinibuffer( new ) ;
    return ( TRUE ) ;
  }
  /* この場合フォーカスが変更されたということだから、カーソルの形状が
     変わる。これらを利用するのは、XIM だけどね。*/
  if( reqw->separateWin.conversion_set_focus ){
    reqw->separateWin.conversion_set_focus = 
      neww->separateWin.conversion_set_focus = 
      curw->separateWin.conversion_set_focus = False ;
    SEPW_DrawScreen( request ) ;
    return FALSE ;
  }
  if( reqw->separateWin.conversion_unset_focus ){
    reqw->separateWin.conversion_unset_focus = 
      neww->separateWin.conversion_unset_focus = 
      curw->separateWin.conversion_unset_focus = False ;
    SEPW_DrawScreen( request ) ;
    return FALSE ;
  }
  return( FALSE ) ;
}

/*
 * 変換クライアントからの「変換属性の修正要求」をさばく関数。
 */
static int SEPW_ChangeAttributes
( Widget gw, unsigned long valuemask, struct ConvAttrs *values )
{
  SeparateWinWidget w   = ( SeparateWinWidget )gw ;
  int ret = False ;

  if( valuemask & CAFocusWindow ){
#ifdef DEBUG
    fprintf
      ( stderr, "(SEPW)Focus window (%ld)\n", values->focus_window ) ;
#endif
    /* フォーカス先の窓って実在するの？ とか実はもう少しきっちりと *
     * 見る必要があると思う。*/
    if( values->focus_window != None ){
      /* 一度設定した筈のフォーカスが別物になっていたら、不味いんでは */
      /* ないかい？ という話です。*/
      if( w->separateWin.focus_window != None ){
	XtDestroyWidget( gw ) ;
	return False ;
      }
      w->separateWin.focus_window = values->focus_window ;
      ret = True ;
    }
  }
  return ret ;
}

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

/*
 * キーが押されたというイベントの処理を行う関数。
 *----
 * キーが離されたという情報は入手不可能な場合があるので、押されたとい
 * う情報だけで上手く動作するように作らなければならない。
 */
static void KeyDownEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;

  if( commonKeyEventHandler( gw, xevent, &( w->separateWin.buffer ) ) )
    SEPW_DrawScreen( gw ) ;
  return ;
}

/*
 * フォーカスが入った／外された時の処理を行う関数。
 */
static void FocusEventHandler
( Widget gw, XEvent *xevent, String *params, Cardinal *num_params )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  /* 画面の修正…カーソルの形状が変化するので。*/
  if( w->separateWin.canvas_probe )
    SEPW_DrawScreen( gw ) ;
  return ;
}

/*
 * 以下のファイルは、SeparateWinWidget のソースの一部である。
 */

/*
 * 差分描画を行っている部分のメインに当たる部分…の関数。
 *---------------
 * 以前に表示された文字列と今回表示しないといけない文字列をじっと見比べて
 * どうかな？ ここの文字は同じだから表示しないで済むね？ とか改行されちゃ
 * ったから、変更はちゃらね？ とか見る…。
 */
static void skkinput_DiffDrawWindowSub1
( Display *disp, Window win,
  struct skkinputManagedFont **fontset, GC gc, GC rgc, 
  struct SKKInputNode *node,
  struct myChar *text, struct myChar *ptext,
  int start_pos, int prev_start_pos,
  int x, int y,
  int width, int height,
  int font_ascent, int font_height, int cursor_width, 
  int isFocus )
{
  int x1, x2, y1, y2, x1diff, x2diff, pos1, pos2 ;
  int isCursorDrawn, nowCursor, cx, cy ;

  cx = cy = -1 ;
  x1 = x2 = x ;
  y1 = y2 = y ;
  pos1 = start_pos ;
  pos2 = prev_start_pos ;
  isCursorDrawn = False ;
  nowCursor = False ;

  /* 二つの文字列を比較する。*/
  while( !IS_END_OF_STRING( *text ) && y1 < height ){
    x1diff = myCharTextWidth( fontset, text, 1 ) ;
    if( x1 == x2 && y1 == y2 && pos1 != node->cur_pos &&
	( node->prev_cx != x1 || node->prev_cy != y1 ) ){
      /* 両者が同じ位置にあるので、同じ charset かつ同じ chara なら現
       * 在の場所に表示する必要はない。*/
      if( text->chara == ptext->chara ){
	/* chara が同じだった場合には、charset が同じかどうかを判別す
	 * る。chara が違えば、charset がどうあれ、同じ文字であること
	 * はない。(と、する！) */
	if( text->charset == ptext->charset ){
	  /* 改行のチェック。*/
	  if( ( x1 + x1diff ) >= width ){
	    x1 =  0 ;
	    y1 += font_height ;
	  }
	  if( node->prev_cx == x1 && node->prev_cy == y1 ){
	    skkwin_jputMyChar
	      ( disp, win, fontset, gc, rgc,
		x1, y1, *text, font_height, font_ascent, False ) ;
	  }
	  x1 += x1diff ;
	  if( IS_ASCII_EQUAL( *text, '\n' ) ){
	    x1 = 0 ;
	    y1 += font_height ;
	  }
	  /* 同じ位置から同じ charset の同じ文字の幅だけカーソルを移動
	   * させたのだから、両者が異なるわけがない。*/
	  x2 = x1 ;
	  y2 = y1 ;
	  /* 次の文字の比較へとうつる。*/
	  text ++ ;  pos1 ++ ;
	  ptext ++ ; pos2 ++ ;
	  continue ;
	}
	/* ここまで来て continue されてなかったら、異なる文字だったと
	 * いうことになる。*/
      }
    } else if( ( x1 > x2 && y1 == y2 ) || y1 > y2 ){
      /* 文字列の最後まで来ていた場合には、問答無用で text の方を表示
       * して欲しいが追いつくまで待ってしまうので、それを胡麻化す。*/
      if( IS_END_OF_STRING( *ptext ) ){
	x2 = width ;
	y2 = height ;
	continue ;
      }
      x2diff = myCharTextWidth( fontset, ptext, 1 ) ;
      if( ( x2 + x2diff ) >= width ){
	x2  = 0 ;
	y2 += font_height ;
      }
      x2 += x2diff ;
      if( IS_ASCII_EQUAL( *ptext, '\n' ) ){
	x2 = 0 ;
	y2 += font_height ;
      }
      ptext ++ ; pos2 ++ ;
      continue ;
    }
    /* 以前に表示した文字列が先行してしまった場合には問答無用で再描
     * 画して良い。また、charset や chara が異なっていた場合にもここに
     * やって来ることになる。*/
    if( ( x1 + x1diff ) >= width ){
      XClearArea
	( disp, win, x1, y1 - font_ascent,
	  width - x1, font_height, False );
      XDrawLine
	( disp, win, gc, x1, y1 - font_ascent,
	  width, y1 - font_ascent + font_height ) ;
      x1 =  0 ;
      y1 += font_height ;
    }
    /* カーソルを表示する場所なのかどうかを判定する。*/
    if( pos1 == node->cur_pos && node->cur_exist ){
      skkinputDrawCursor
	( disp, win, gc, rgc,
	  x1, y1, font_ascent, x1diff, font_height, isFocus ) ;
      nowCursor = isCursorDrawn = True ;
      cx = x1 ; cy = y1 ;
    }
    if( IS_ASCII_EQUAL( *text, '\n' ) ){
      if( nowCursor )
	x1 += x1diff ;
      if( x1 < width )
	XClearArea
	  ( disp, win, x1, y1 - font_ascent,
	    width - x1, font_height, False ) ;
      x1 = 0 ;
      y1 += font_height ;
    } else {
      if( nowCursor ){
	if( isFocus ){
	  skkwin_jputMyChar
	    ( disp, win, fontset, rgc, gc, 
	      x1, y1, *text, font_height, font_ascent, True ) ;
	} else {
	  skkwin_jputMyChar
	    ( disp, win, fontset, gc, rgc, 
	      x1, y1, *text, font_height, font_ascent, True ) ;
	}
      } else {
	skkwin_jputMyChar
	  ( disp, win, fontset, gc, rgc, 
	    x1, y1, *text, font_height, font_ascent, False ) ;
      }
      /* 表示したキャラの分だけカーソルを移動させる。*/
      x1 += x1diff ;
    }
    nowCursor = False ;
    text ++ ;
    pos1 ++ ;
  }
  /* まだカーソルが書かれていない場合の処理。*/
  if( !isCursorDrawn && node->cur_exist ){
    if( ( x1 + cursor_width ) >= width ){
      XClearArea
	( disp, win, x1, y1 - font_ascent, 
	  width - x1, font_height, False ) ;
      XDrawLine
	( disp, win, gc, x1, y1 - font_ascent,
	  width, y1 - font_ascent + font_height ) ;
      x1 =  0 ;
      y1 += font_height ;
    }
    /* カーソルを描画する。*/
    skkinputDrawCursor
      ( disp, win, gc, rgc,
	x1, y1, font_ascent, cursor_width, font_height, isFocus ) ;
    cx = x1 ; cy = y1 ;
    x1 += cursor_width ;
    isCursorDrawn = True ;
  }
#ifdef DEBUG
  printf( "(DiffDraw): %d, %d / %d, %d / %d\n",
	  x1, y1, x2, y2, IS_END_OF_STRING( *ptext ) ) ;
#endif
  /* 以前の描画に追い付くことなく終了してしまった場合の処理。*/
  if( y1 < height && ( !IS_END_OF_STRING( *ptext ) || 
		       x1 <= node->prev_cx || y1 <= node->prev_cy ) ){
    XClearArea
      ( disp, win, x1, y1 - font_ascent, width - x1, font_height, False ) ;
    y1 += font_height ;
    if( y1 < height )
      XClearArea
	( disp, win, 0, y1 - font_ascent, width, height - y1, False ) ;
  }
  /* カーソル位置を記憶しておく。*/
  if( isCursorDrawn ){
    node->prev_cx = cx ;
    node->prev_cy = cy ;
  }
  return ;
}

/*
 * 差分表示を行う関数。
 *----
 * カーソル表示を除外してますけど、カーソルを表示するためには、
 * 「textbuffer の中のどの位置から表示しようとしているか」を渡す必要が
 * あります。まだ、その部分のコードはありません。
 */
static void skkinput_DiffDrawWindow
( Widget gw, struct SKKInputNode *node,
  struct myChar *text, struct myChar *ptext,
  int startpos,
  int x, int y, int width, int height )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;

  /* 両者が共に表示されている部分について差分描画を行う。*/
  skkinput_DiffDrawWindowSub1
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      w->separateWin.fontset,
      w->separateWin.gc, w->separateWin.rgc,
      node, 
      text, ptext,
      startpos, node->prev_start_pos,
      x, y, 
      width, height, 
      w->separateWin.font_ascent, w->separateWin.font_height,
      w->separateWin.cursor_width,
      w->separateWin.is_focus ) ;
  return ;
}

static int SEPW_FocusP( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  Window input_window ;
  int revert ;
  
  XGetInputFocus( XtDisplay( gw ), &input_window, &revert ) ;
  if( input_window == XtWindow( gw ) ||
      input_window == XtWindow( w->separateWin.canvas_popup ) ||
      input_window == w->separateWin.focus_window ){
    return True ;
  }
  return False ;
}

/*
 * skkinput の画面を差分表示を用いて表示する関数。
 *-----
 * 通常の画面の修正(変更)はこちらの関数によって行われる。
 */
static void SEPW_DrawScreen( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  int cur_line, linenum, maxline, spos, sty, edy ;
  unsigned int width, height ;
  struct myChar *sptr, *dptr ;
  struct SKKInputNode *node, *mNode ;

  node = w->separateWin.buffer.topbuffer ;

  /* 以前カーソルが存在していた場所。*/
  w->separateWin.is_focus = SEPW_FocusP( gw ) ;

  /* Window の size を獲得する。*/
  width  = w->separateWin.canvas->core.width ;
  height = w->separateWin.canvas->core.height ;

  /* 最大表示可能行数を求める。*/
  maxline = height / w->separateWin.font_height ;
  /* 現在、テキストは何行まで表示しなければならないのかを算出する。*/
  linenum = skkinput_GetLineNum
    ( w->separateWin.fontset, node->textbuffer,
      width, w->separateWin.cursor_width, node->cur_pos, &cur_line ) ;

  /* モードラインと minibuffer のために二行必要であるから削る。*/
  maxline = ( maxline < 3 )? 1 : maxline - 2 ;

  /* 表示可能行数を超えていた場合の処理。カーソル位置が入る範囲の内 *
   * で一番最初の行の番号が最も小さいくなるように設定する。*/
  if( linenum >= maxline && cur_line >= maxline ){
    int startline = cur_line - maxline + 1 ;
    /* 指定された場所から表示しなればならないので、その位置を算出する。*/
    spos = skkinput_FindStartLine
      ( w->separateWin.fontset, node->textbuffer, startline, width ) ;
    dptr = node->textbuffer + spos ;
    sptr = w->separateWin.pwrite_string + node->prev_start_pos ;
  }  else {
    spos = 0 ;
    /* 共に一番最初の文字から表示可能である。*/
    dptr = node->textbuffer ;
    sptr = w->separateWin.pwrite_string + node->prev_start_pos ;
  }    
  sty = w->separateWin.font_ascent ;
  edy = sty + maxline * w->separateWin.font_height ;
  /* Window の buffer に文字列を表示する。*/
  skkinput_DiffDrawWindow
    ( gw, node, dptr, sptr, spos, 0, sty, width, edy ) ;
  myCharStrcpy( w->separateWin.pwrite_string, node->textbuffer ) ;
  node->prev_start_pos = spos ;

  /* モードラインを表示する。差分表示するためには前回表示した文字列の
   * 全てを覚えている必要があるけどそこまでする気になれないのでしない
   * ことにする。*/
  sty = edy ;
  edy = sty + w->separateWin.font_height ;
  skkinput_DiffDrawModeLine( gw, node, 0, sty, width ) ;
			 
  /* mini-buffer を表示する。*/
  mNode = w->separateWin.buffer.lastbuffer ;

  sty = edy ;
  edy = sty + w->separateWin.font_height ;

  /* for debugging */ 
  if( mNode == node ){
    skkwin_jdiffputstring
      ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
	w->separateWin.fontset,
	w->separateWin.gc, w->separateWin.rgc, 
	0, sty, width,
	node->mtextbuffer, w->separateWin.pmtextbuffer,
	w->separateWin.font_height,
	w->separateWin.font_ascent ) ;
    myCharStrcpy( w->separateWin.pmtextbuffer, node->mtextbuffer ) ;
  } else {
    if( !IS_END_OF_STRING( mNode->mtextbuffer[ 0 ] ) ){
      skkwin_jdiffputstring
	( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
	  w->separateWin.fontset,
	  w->separateWin.gc, w->separateWin.rgc, 
	  0, sty, width,
	  mNode->mtextbuffer, w->separateWin.pmtextbuffer,
	  w->separateWin.font_height,
	  w->separateWin.font_ascent ) ;
      myCharStrcpy( w->separateWin.pmtextbuffer, mNode->mtextbuffer ) ;
    } else {
      linenum = skkinput_GetLineNum
	( w->separateWin.fontset, mNode->textbuffer,
	  width, w->separateWin.cursor_width,
	  mNode->cur_pos, &cur_line ) ;
      spos = skkinput_FindStartLine
	( w->separateWin.fontset, 
	  mNode->textbuffer, cur_line, width ) ;
      /* Window の buffer に文字列を表示する。*/
      skkinput_DiffDrawWindow
	( gw, mNode, mNode->textbuffer + spos,
	  w->separateWin.pmtextbuffer, spos,
	  0, sty, width, edy ) ;
      myCharStrcpy
	( w->separateWin.pmtextbuffer, mNode->textbuffer + spos ) ;
    }
  }
  return ;
}

/*
 * skkinput の窓を表示する関数。
 *-----
 * こちらは差分描画を行わない。
 */
static void skkinput_FullDrawWindow
( Display *disp, Window win,
  struct skkinputManagedFont **fontset, 
  GC gc, GC rgc,
  struct SKKInputNode *node,
  struct myChar *text, int startpos,
  int x, int y, int width, int height,
  int cursor_width, int font_ascent, int font_height,
  int isFocus )
{
  int pos, isCursorDrawn, cx, cy, xdiff ;

  cx = cy = -1 ;
  pos           = startpos ;
  isCursorDrawn = False ;

  while( !IS_END_OF_STRING( *text ) ){
    xdiff = myCharTextWidth( fontset, text, 1 ) ;
    /* カーソルを表示する場所なのかどうかを判定する。*/
    if( pos == node->cur_pos && node->cur_exist ){
      skkinputDrawCursor
	( disp, win, gc, rgc,
	  x, y, font_ascent, xdiff, font_height, isFocus ) ;
      isCursorDrawn = True ;
      cx = x ; cy = y ;
    }
    if( ( x + xdiff ) >= width ){
      XDrawLine
	( disp, win, gc, x, y - font_ascent,
	  width, y - font_ascent + font_height ) ;
      x =  0 ;
      y += font_height ;
      /* 画面の下まで書き切ったのかどうかを判断する。*/
      if( y >= height )
	goto exit_loop ;
    }
    /* 現在表示した文字が改行コードだった場合の処理。*/
    if( IS_ASCII_EQUAL( *text, '\n' ) ){
      x =  0 ;
      y += font_height ;
      /* 画面の下まで書き切ったのかどうかを判断する。*/
      if( y >= height )
	goto exit_loop ;
    } else {
      if( isCursorDrawn ){
	/* カーソルの表示位置は白黒が反転する必要がある。*/
	if( isFocus ){
	  skkwin_jputMyChar
	    ( disp, win, fontset, rgc, gc, 
	      x, y, *text, font_height, font_ascent, True ) ;
	} else {
	  skkwin_jputMyChar
	    ( disp, win, fontset, gc, rgc, 
	      x, y, *text, font_height, font_ascent, True ) ;
	}
	isCursorDrawn = False ;
      } else {
	skkwin_jputMyChar
	  ( disp, win, fontset, gc, rgc,
	    x, y, *text, font_height, font_ascent, False ) ;
      }
      x += xdiff ;
    }
    text ++ ;
    pos ++ ;
  }
  /* 最後までカーソルが表示されていなかったのなら、最後の行にカーソ *
   * ルを表示する。*/
  if( pos == node->cur_pos && node->cur_exist ){
    /* カーソル改行の必要あり？ */
    if( ( x + cursor_width ) >= width ){
      XDrawLine
	( disp, win, gc, x, y - font_ascent,
	  width, y - font_ascent + font_height ) ;
      x  = 0 ;
      y += font_height ;
    }
    skkinputDrawCursor
      ( disp, win, gc, rgc, x, y, font_ascent,
	cursor_width, font_height, isFocus ) ;
    cx = x ; cy = y ;
  }
exit_loop:
  node->prev_cx = cx ; 
  node->prev_cy = cy ;
  return ;
}

static void skkinput_DiffDrawModeLine
( Widget gw, struct SKKInputNode *node, int x, int y, unsigned int width )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  unsigned long modeline ;

  modeline = 
    ( skkinput_GetModelineNumber( node ) |
      ( ( ( w->separateWin.buffer.jisyo_dirty )? 4 : 0 ) ) |
      ( ( ( w->separateWin.buffer.egg_like_newline )? 8 : 0 ) ) |
      ( ( ( w->separateWin.buffer.overthespot_like_input )? 16 : 0 ) ) ) ;
  if( modeline == w->separateWin.prev_modeline )
    return ;
  skkinput_FullDrawModeLine( gw, node, x, y, width ) ;
  return ;
}

static void skkinput_FullDrawModeLine
( Widget gw, struct SKKInputNode *node, int x, int y, unsigned int width )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  int modeline = skkinput_GetModelineNumber( node ) ;
  int new_x, modeno ;

  /* まず、モードラインの表示してある場所を消去する。*/
  XFillRectangle
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      w->separateWin.gc,
      x, y - w->separateWin.font_ascent,
      width, w->separateWin.font_height ) ;

  /* モードラインを表示する( SKKのモード表示 )。*/
  x = skkwin_jputCharString
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ), 
      w->separateWin.fontset, w->separateWin.rgc,
      x, y, modelineFullStrings[ modeline - 1 ] ) ;
  /* モードラインを表示する( 辞書及び入力方法の情報を表示 )。*/
  x = skkwin_jputCharString
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ), 
      w->separateWin.fontset, w->separateWin.rgc, x, y,
      modelineDirtyStrings
      [ ( ( w->separateWin.buffer.jisyo_dirty )? 1 : 0 ) ] ) ;
  x = skkwin_jputCharString
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ), 
      w->separateWin.fontset, w->separateWin.rgc, x, y, "-Skkinput:" ) ;
  /* メージャモードの表示。*/
  x = skkwin_jputCharString
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      w->separateWin.fontset, w->separateWin.rgc, x, y,
      majorModeStrings
      [ ( ( w->separateWin.buffer.chat_adapter )? 1 : 0 ) ] ) ;
  /* マイナーモードの表示(...複数あったらこんな付け焼刃んなコードじゃ *
   * やられそうです。) *
   * 若葉    「そうなんですか？」
   * フィリー「あんた状況分かってんの？」
   * 若葉    「申し訳ございません。景色に見とれていて話についていって
   *           ませんでした。」*/
  modeno = 
    ( ( ( w->separateWin.buffer.egg_like_newline )? 1 : 0 ) |
      ( ( w->separateWin.buffer.overthespot_like_input )? 2 : 0 ) ) ;
  x = skkwin_jputCharString
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ), 
      w->separateWin.fontset, w->separateWin.rgc, x, y,
      minorModeStrings[ modeno ] ) ;
  while( x < width ){
    new_x = skkwin_jputchar
      ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
	w->separateWin.fontset, w->separateWin.rgc, x, y, '-', False ) ;
    /* もし 1 dot も進まないのだったら、無効。*/
    if( new_x == x )
      break ;
    x = new_x ;
  }
  w->separateWin.prev_modeline = 
    ( modeline | ( w->separateWin.buffer.jisyo_dirty << 2 ) |
      ( modeno << 3 ) ) ;
  return ;
}

/*
 * ミニバッファに表示されていたものを消去する関数。
 */
static void SEPW_ClearMinibuffer( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  Window root ;
  int x, y, maxline ;
  unsigned int width, height, border, depth ;

  /* Window の size を獲得する。*/
  XGetGeometry
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
     &root, &x, &y, &width, &height, &border, &depth ) ;

  /* 最大表示可能行数を求める。*/
  maxline = height / w->separateWin.font_height ;
  if( maxline < 3 ){
    y = 2 * w->separateWin.font_height ;
  } else {
    y = ( maxline - 1 ) * w->separateWin.font_height ;
  }
  MYCHAR_SET_END_OF_STRING( w->separateWin.pmtextbuffer[ 0 ] ) ;
#if 0
  XFillRectangle
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      w->separateWin.rgc,
      0, y, width, w->separateWin.font_height ) ;
#else
  XClearArea
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      0, y, width, w->separateWin.font_height, False ) ;
#endif
  return ;
}

/*
 * skkinput の画面をいちいち全部書き直す関数。
 *-----
 * 画面の大きさの変更などが行われた時にはこちらの関数が呼ばれることに
 * なる。
 */
static void SEPW_RedrawScreen( Widget gw )
{
  SeparateWinWidget w = ( SeparateWinWidget )gw ;
  int x, y, cur_line, linenum, maxline, spos, sty, edy ;
  Window root ;
  unsigned int width, height, border, depth ;
  struct myChar *ptr ;
  struct SKKInputNode *mNode, *node = w->separateWin.buffer.topbuffer ;

  /* 以前カーソルが存在していた場所。*/
  w->separateWin.is_focus = SEPW_FocusP( gw ) ;

  /* Window の中身を消去する。*/
  XClearWindow( XtDisplay( gw ), XtWindow( w->separateWin.canvas ) ) ;

  /* Window の size を獲得する。*/
  XGetGeometry
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
     &root, &x, &y, &width, &height, &border, &depth ) ;

  /* 最大表示可能行数を求める。*/
  maxline = height / w->separateWin.font_height ;
  /* 現在、テキストは何行まで表示しなければならないのかを算出する。*/
  linenum = skkinput_GetLineNum
    ( w->separateWin.fontset, node->textbuffer, 
      width, w->separateWin.cursor_width, node->cur_pos, &cur_line ) ;
  /* モードラインと minibuffer のために二行必要であるから削る。*/
  maxline = ( maxline < 3 )? 1 : maxline - 2 ;
  /* 表示可能行数を超えていた場合の処理。カーソル位置が入る範囲の内 *
   * で一番最初の行の番号が最も小さいくなるように設定する。*/
  if( linenum >= maxline && cur_line >= maxline ){
    int startline = cur_line - maxline + 1 ;
    /* 指定された場所から表示しなればならないので、その位置を算出する。*/
    spos = skkinput_FindStartLine
      ( w->separateWin.fontset, node->textbuffer, startline, width ) ;
    ptr = node->textbuffer + spos ;
  }  else {
    spos = 0 ;
    /* 共に一番最初の文字から表示可能である。*/
    ptr = node->textbuffer ;
  }    
  sty = w->separateWin.font_ascent ;
  edy = sty + maxline * w->separateWin.font_height ;
  /* Window の buffer に文字列を表示する。*/
  skkinput_FullDrawWindow
    ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
      w->separateWin.fontset, 
      w->separateWin.gc, w->separateWin.rgc,
      node, ptr, spos, 0, sty, width, edy, 
      w->separateWin.cursor_width,
      w->separateWin.font_ascent, w->separateWin.font_height,
      w->separateWin.is_focus ) ;
  myCharStrcpy( w->separateWin.pwrite_string, node->textbuffer ) ;

  /* モードラインを表示する。差分表示に直に対応する予定ではある。*/
  sty = edy ;
  edy = sty + w->separateWin.font_height ;
  skkinput_FullDrawModeLine( gw, node, 0, sty, width ) ;
			 
  /* mini-buffer を表示する。*/
  mNode = w->separateWin.buffer.lastbuffer ;
  sty = edy ;
  edy = sty + w->separateWin.font_height ;

  if( mNode == node ){
    skkwin_jputstring
      ( XtDisplay( gw ), XtWindow( w->separateWin.canvas ), 
	w->separateWin.fontset, w->separateWin.gc,
	0, sty, mNode->mtextbuffer ) ;
    myCharStrcpy( w->separateWin.pmtextbuffer, node->mtextbuffer ) ;
  } else {
    if( !IS_END_OF_STRING( mNode->mtextbuffer[ 0 ] ) ){
      skkwin_jputstring
	( XtDisplay( gw ), XtWindow( w->separateWin.canvas ), 
	  w->separateWin.fontset, w->separateWin.gc,
	  0, sty, mNode->mtextbuffer ) ;
      myCharStrcpy( w->separateWin.pmtextbuffer, mNode->mtextbuffer ) ;
    } else {
      linenum = skkinput_GetLineNum
	( w->separateWin.fontset, mNode->textbuffer, 
	  width, w->separateWin.cursor_width, mNode->cur_pos, &cur_line ) ;
      /* 指定された場所から表示しなればならないので、その位置を算出する。*/
      spos = skkinput_FindStartLine
	( w->separateWin.fontset, mNode->textbuffer, cur_line, width ) ;
      /* Window の buffer に文字列を表示する。*/
      skkinput_FullDrawWindow
	( XtDisplay( gw ), XtWindow( w->separateWin.canvas ),
	  w->separateWin.fontset, 
	  w->separateWin.gc, w->separateWin.rgc,
	  mNode,
	  mNode->textbuffer + spos, spos, 0, sty, width, edy, 
	  w->separateWin.cursor_width,
	  w->separateWin.font_ascent, w->separateWin.font_height,
	  w->separateWin.is_focus ) ;
      MYCHAR_SET_END_OF_STRING( w->separateWin.pmtextbuffer[ 0 ] ) ;
    }
  }
  return ;
}

/*
 * 分離窓の再描画を行うに呼び出されるコールバック関数。
 */
static void SeparateCanvas_RedisplayCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  SEPW_RedrawScreen( ( Widget )client ) ;
  return ;
}

/*
 * Canvas Widget を壊す時に呼び出されるコールバック関数。
 */
static void Canvas_DestroyCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  XtRemoveAllCallbacks( gw, XtNredrawNotify ) ;
  XtRemoveAllCallbacks( gw, XtNdestroyNotify ) ;
  return ;
}

static void WMMessageCloseCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  /* 全てのコールバック関数を破棄しておく。*/
  XtRemoveAllCallbacks( gw, XtNwmcloseNotify ) ;
  XtRemoveAllCallbacks( gw, XtNdestroyNotify ) ;
  XtDestroyWidget( ( Widget )client ) ;
  return ;
}

static void WMShellDestroyCallback
( Widget gw, caddr_t client, caddr_t caller )
{
  /* 全てのコールバック関数を破棄しておく。*/
  XtRemoveAllCallbacks( gw, XtNwmcloseNotify ) ;
  XtRemoveAllCallbacks( gw, XtNdestroyNotify ) ;
  return ;
}

static void
SeparateWin_fixcallback (
	register Widget		gw,
	register XtPointer	client,
	register XtPointer	caller)
{
	XtCallCallbacks (gw, XtNtextNotify, caller) ;
	return ;
}

