/* # skkinput (Simple Kana-Kanji Input)
 * MyDispatch.c --- Dispatch X Event
 * 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 <sys/types.h>
#include <X11/Xlib.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>

#include "MyDispatch.h"
#include "MyError.h"

#define MAX_EVENTTYPES		(10)

struct my_eventtable {
  Window dest_window ;
  Window src_window ;
  long event_mask ;
  int number_of_eventtypes ;
  int event_type[ MAX_EVENTTYPES ] ;
  struct my_eventtable *next ;
} ;

static struct my_eventtable *my_eventtable_listtop = NULL ;

/*
 *
 */
static struct my_eventtable *find_whether_myeventhandler_exists
( Window win )
{
  struct my_eventtable *node ;

  /* 指定されたウィンドウに対して、既にイベントハンドラは存在するのか？ */
  for( node = my_eventtable_listtop ; node != NULL ; node = node->next ){
    if( node->src_window == win ){
      return node ;
    }
  }
  /* 何も見付からなかった場合。*/
  return NULL ;
}

static struct my_eventtable *alloc_myeventhandler( void )
{
  struct my_eventtable *node ;
  
  /* メモリを確保する…。*/
  if( ( node = malloc( sizeof( struct my_eventtable ) ) ) == NULL ){
    /* ここに来てしまったら、もうどうしようもないから終了する。*/
    fprintf( stderr, "Memory fault.\n" ) ;
    exit( 1 ) ;
  }
  node->src_window = node->dest_window = None ;
  node->event_mask = NoEventMask ;

  node->number_of_eventtypes = 0 ;

  node->next            = my_eventtable_listtop ;
  my_eventtable_listtop = node ;
  return node ;
}

/*
 * 自前のイベント処理が必要になった時に呼ばれる関数。 
 *------
 * 対象となるウィンドウ、奪うべきイベントの種類、その時のマスク。
 */
int add_myeventhandler
( Display *disp, Window src_win, Window dest_win,
  int event_type, long event_mask )
{
	struct my_eventtable *node ;
	int i ;

	if( ( node = find_whether_myeventhandler_exists( src_win ) ) != NULL ){
		/* 既にそのイベントは奪うように指示されているかもしれないから、*
		 * チェックする  */
		for( i = 0 ; i < node->number_of_eventtypes ; i ++ ){
			/* 既に登録済みでしょうか？ */
			if( node->event_type[ i ] == event_type )
				return True ;
		}
		/* イベントを登録して、そのイベントを受け取るよう指示する。*/
		node->event_type[ node->number_of_eventtypes ++ ] = event_type ;
		node->event_mask |= event_mask ;
		XSelectInput( disp, src_win, node->event_mask ) ;
		return True ;
	}
	node = alloc_myeventhandler() ;
	node->src_window           = src_win ;
	node->dest_window          = dest_win ;
	node->event_mask           = event_mask ;
	node->event_type[ 0 ]      = event_type ;
	node->number_of_eventtypes = 1 ;
	XSelectInput( disp, src_win, event_mask ) ;
	return True ;
}

/*
 * 自前のイベント処理を停止する時に呼び出す関数。
 */
int remove_allmyeventhandler
( Display *disp, Window src_win )
{
	struct my_eventtable *node, *pNode ;
	/* そもそも中身が無いのじゃないかと疑ってみる。*/
	if( ( node = my_eventtable_listtop ) == NULL )
		return False ;
	
	pNode = NULL ;
	while( node != NULL ){
		/* 消去すべき event が見付かった場合…。*/
		if( node->src_window == src_win ){
			if( pNode == NULL ){
				/* 消去すべきものがリストの先頭だった場合。*/
				my_eventtable_listtop = node->next ;
				/* もはやイベントを受け取らないようにする。*/
				XSafeSelectInput( disp, src_win, NoEventMask ) ;
				/* メモリの解放。*/
				free( node ) ;
				return True ;
			} else {
				/* リストの途中だった場合。*/
				pNode->next = node->next ;
				/* もはやイベントを受け取らないようにする。*/
				XSafeSelectInput( disp, src_win, NoEventMask ) ;
				/* メモリの解放。*/
				free( node ) ;
				return True ;
			}
		}
		pNode = node ;
		node  = node->next ;
	}
	/* そんなウィンドウは知りません。*/
	return False ;
}


/*
 * 自前のイベント処理を停止する時に呼び出す関数。
 */
int remove_myeventhandler
( Display *disp, Window src_win, int event_type, 
  unsigned long event_mask )
{
	struct my_eventtable *node, *pNode ;
	int i ;

	/* そもそも中身が無いのじゃないかと疑ってみる。*/
	if( ( node = my_eventtable_listtop ) == NULL )
		return False ;

	pNode = NULL ;
	while( node != NULL ){
		/* 消去すべき event が見付かった場合…。*/
		if( node->src_window == src_win ){
			node->event_mask &= ~event_mask ;
			for( i = 0 ; i < MAX_EVENTTYPES ; i ++ ){
				if( node->event_type[ i ] == event_type )
					node->event_type[ i ] = NO_EVENT_HANDLE ;
			}
			XSafeSelectInput( disp, src_win, node->event_mask ) ;
			if( node->event_mask == NoEventMask ){
				if( pNode == NULL ){
					/* 消去すべきものがリストの先頭だった場合。*/
					my_eventtable_listtop = node->next ;
				} else {
					/* リストの途中だった場合。*/
					pNode->next = node->next ;
				}
			}
			return True ;
		}
		pNode = node ;
		node  = node->next ;
	}
	/* そんなウィンドウは知りません。*/
	return False ;
}

/*
 * イベントを適切なウィジェットへと送り届ける関数。
 */
int mydispatch_event( Display *disp, XEvent *xevent )
{
	struct my_eventtable *node ;
	int i ;

	/* まず捜してみる。*/
	node = find_whether_myeventhandler_exists( xevent->xany.window ) ;
	/* 見付かった場合には…。*/
	if( node != NULL ){
		/* そのウィンドウに対して、どのイベントを取るかの指定がある筈な *
		 * ので、それをチェックする。*/
		for( i = 0 ; i < node->number_of_eventtypes ; i ++ ){
			/* 見付かりましたか？ */
			if( node->event_type[ i ] == xevent->type ){
				if( xevent->type == KeyPress || xevent->type == KeyRelease ){
					xevent->xkey.window    = node->dest_window ;
					xevent->xkey.subwindow = None ;
				} else {
					xevent->xany.window = node->dest_window ;
				}
				XSafeSendEvent
					( disp, xevent->xany.window, False, NoEventMask, xevent ) ; 
				break ;
			}
		}
	} else {
		/* イベントを発行する。*/
		XtSafeDispatchEvent( xevent ) ;
	}
	return True ;
}

