/* 
 * tkUnixIm.c --
 *
 *      This file contains modules to implement the XIM protocol session.
 *	This is the shrinked version of tkXIM.c, worte for tk8.0jp.
 *
 *	Author:	m-hirano@sra.co.jp
 *
 * Copyright 1999 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.
 */

#if 0
static char rcsid[] = "$Header: /home/m-hirano/cvsroot/tcltk/tk8.1/unix/tkUnixIm.c,v 1.1 1999/05/10 00:39:16 m-hirano Exp $";
#endif

#include "tkPort.h"
#include "tkInt.h"

#ifdef TK_USE_INPUT_METHODS
#ifdef I18N_IMPROVE

static void		TkpIMGenericHandler _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
static TkWindow *	GetToplevel _ANSI_ARGS_((TkWindow *winPtr));
static void		TkpCreateIMGenericHandler _ANSI_ARGS_((Tk_Window tkwin));
static void		TkpDeleteIMGenericHandler _ANSI_ARGS_((Tk_Window tkwin));

static TkWindow *
GetToplevel(winPtr)
     TkWindow *winPtr;
{
    while (!(winPtr->flags & TK_TOP_LEVEL)) {
	winPtr = winPtr->parentPtr;
	if (winPtr == NULL) {
	    return NULL;
	}
    }
    return winPtr;
}


static void
TkpIMGenericHandler(clientData, eventPtr)
     ClientData clientData;
     XEvent *eventPtr;
{
    Tk_Window tkwin = (Tk_Window)clientData;
    TkWindow *winPtr = (TkWindow *)clientData;
    
    if (eventPtr->xany.window != Tk_WindowId(tkwin) ||
	winPtr->inputContext == NULL ||
	!(winPtr->flags & TK_CHECKED_IC)) {
	/*
	 * Why ME ???
	 */
	return;
    }

    /*
     * Well, should I care about Enter/Leave ?
     */

    switch (eventPtr->type) {
        case KeyPress:
        case FocusIn: {
	    Window root, child;
	    int rootX, rootY;
	    int wX, wY;
	    unsigned int mask;

	    /*
	     * Care about case mouse pointer is not on winPtr->window.
	     * In such a case, IM server can't fetch any events from Tk.
	     */

	    if (XQueryPointer(winPtr->display,
			      RootWindow(winPtr->display, winPtr->screenNum),
			      &root, &child, &rootX, &rootY, &wX, &wY, &mask) == True) {
		TkWindow *pWin = (TkWindow *)Tk_CoordsToWindow(rootX, rootY,
							       (Tk_Window)winPtr);
		if (pWin != NULL && (pWin->window != winPtr->window)) {
#if 0
		    /*
		     * Code belows are what I REALLY want to do. But,
		     * in XIMPreeditPosition mode, IM server use the
		     * focus window as key event source window and as
		     * PreeditArea window by X11 specification. I want
		     * IM server to use this focus window ONLY as key
		     * event source. Means:
		     *
		     * 	o PreeditArea and PreeditPosition take place
		     * 	  within client window (winPtr->window).
		     *	o KeyPress event source is the window in which
		     *	  mouse pointer is (pWin->window).
		     */

		    if (XSetICValues(winPtr->inputContext, XNFocusWindow,
				     pWin->window, NULL) != NULL) {
			fprintf(stderr, "debugIC: can't set IC focus to pointer window 0x%08x\n",
				pWin->window);
		    } else {
			fprintf(stderr, "debugIC: set IC focus to pointer window 0x%08x\n",
				pWin->window);
		    }
#endif
		    /*
		     * Check pWin and winPtr are in same toplevel.
		     * If they are NOT, don't change focus. 
		     */
		    TkWindow *pTop = GetToplevel(pWin);
		    TkWindow *wTop = GetToplevel(winPtr);
		    if (pTop == wTop) {
			TkpChangeFocus(winPtr, 1);
		    }
		}
	    }
	    /*
	     * Get current focused window.
	     */
	    if (winPtr->dispPtr->lastFocusedIC != winPtr->inputContext) {
		winPtr->dispPtr->lastFocusedIC = winPtr->inputContext;
		XSetICFocus(winPtr->inputContext);
	    }
	    break;
	}

        case FocusOut: {
	    winPtr->dispPtr->lastFocusedIC = None;
	    XUnsetICFocus(winPtr->inputContext);
	    break;
	}

	case DestroyNotify: {
	    winPtr->dispPtr->lastFocusedIC = NULL;
	    XUnsetICFocus(winPtr->inputContext);
	    TkpDeleteIMGenericHandler(tkwin);
	    break;
	}
    }
}


static void
TkpCreateIMGenericHandler(tkwin)
     Tk_Window tkwin;
{
    Tk_CreateEventHandler(tkwin, FocusChangeMask|StructureNotifyMask|KeyPressMask
#if 0
			  EnterWindowMask|LeaveWindowMask,
#else
			  ,
#endif
			  (Tk_EventProc *)TkpIMGenericHandler,
			  (ClientData)tkwin);
}


static void
TkpDeleteIMGenericHandler(tkwin)
     Tk_Window tkwin;
{
    Tk_DeleteEventHandler(tkwin, FocusChangeMask|StructureNotifyMask|KeyPressMask
#if 0
			  EnterWindowMask|LeaveWindowMask,
#else
			  ,
#endif
			  (Tk_EventProc *)TkpIMGenericHandler,
			  (ClientData)tkwin);
}


XIC
TkpCreateIC(winPtr)
     TkWindow *winPtr;
{
    if (winPtr->dispPtr->inputMethod != NULL &&
	!(winPtr->flags & TK_CHECKED_IC)) {
	winPtr->inputContext =
		XCreateIC(winPtr->dispPtr->inputMethod,
			  XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
			  XNClientWindow, winPtr->window,
			  XNFocusWindow, winPtr->window,
			  NULL);
	if (winPtr->inputContext != NULL) {
	    TkpCreateIMGenericHandler((Tk_Window)winPtr);
	}
    }
    winPtr->flags |= TK_CHECKED_IC;
    return winPtr->inputContext;
}


void
TkpDestroyIC(winPtr, needDestroy)
     TkWindow *winPtr;
     int needDestroy;
{
    TkpDeleteIMGenericHandler((Tk_Window)winPtr);
    if (winPtr->inputContext == winPtr->dispPtr->lastFocusedIC) {
	winPtr->dispPtr->lastFocusedIC = None;
    }
    if (needDestroy == 1 &&
	winPtr->inputContext != NULL) {
	XDestroyIC(winPtr->inputContext);
    }
    winPtr->flags &= ~(TK_CHECKED_IC);
    winPtr->inputContext = NULL;
}


int
Tk_ImconfigureObjCmd(clientData, interp, objc, objv)
     ClientData clientData;	/* Main window associated with
				 * interpreter. */
     Tcl_Interp *interp;	/* Current interpreter. */
     int objc;			/* Number of arguments. */
     Tcl_Obj *CONST objv[];	/* Argument objects. */
{
    Tk_Window tkwin = (Tk_Window)clientData;
    Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);

    if (objc < 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "path ?option? ?arg? ...");
	return TCL_ERROR;
    }

    tkwin = Tk_NameToWindow(interp, Tcl_GetStringFromObj(objv[1], NULL), tkwin);
    if (tkwin == NULL) {
	return TCL_ERROR;
    }

    if (((TkWindow *)tkwin)->dispPtr->inputMethod == NULL) {
	Tcl_AppendStringsToObj(resultPtr, "No IM server is available.", NULL);
	return TCL_ERROR;
    }

    Tcl_AppendStringsToObj(resultPtr,
			  (TkpCreateIC((TkWindow *)tkwin) != NULL) ? "1" : "0",
			  NULL);
    return TCL_OK;
}

#endif /* I18N_IMPROVE */
#endif /* TK_USE_INPUT_METHODS */
