/*
 * tkWinEmbed.c --
 *
 *	This file contains platform specific procedures for Windows platforms
 *	to provide basic operations needed for application embedding (where
 *	one application can use as its main window an internal window from
 *	another application).
 *
 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tkWinEmbed.c,v 1.31 2006/05/29 22:21:40 hobbs Exp $
 */

#include "tkWinInt.h"

/*
 * One of the following structures exists for each container in this
 * application. It keeps track of the container window and its associated
 * embedded window.
 */

typedef struct Container {
    HWND parentHWnd;		/* Windows HWND to the parent window */
    TkWindow *parentPtr;	/* Tk's information about the container or
				 * NULL if the container isn't in this
				 * process. */
    HWND embeddedHWnd;		/* Windows HWND to the embedded window. */
    TkWindow *embeddedPtr;	/* Tk's information about the embedded window,
				 * or NULL if the embedded application isn't
				 * in this process. */
    HWND embeddedMenuHWnd;	/* Tk's embedded menu window handler. */
    struct Container *nextPtr;	/* Next in list of all containers in this
				 * process. */
} Container;

typedef struct ThreadSpecificData {
    Container *firstContainerPtr;
				/* First in list of all containers managed by
				 * this process. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;

static void		ContainerEventProc(ClientData clientData,
			    XEvent *eventPtr);
static void		EmbeddedEventProc(ClientData clientData,
			    XEvent *eventPtr);
static void		EmbedGeometryRequest(Container *containerPtr,
			    int width, int height);
static void		EmbedWindowDeleted(TkWindow *winPtr);
static void		Tk_MapEmbeddedWindow(TkWindow* winPtr);
HWND			Tk_GetEmbeddedHWnd(TkWindow* winPtr);

/*
 *----------------------------------------------------------------------
 *
 * TkWinCleanupContainerList --
 *
 *	Finalizes the list of containers.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Releases memory occupied by containers of embedded windows.
 *
 *----------------------------------------------------------------------
 */

void
TkWinCleanupContainerList(void)
{
    Container *nextPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    for (; tsdPtr->firstContainerPtr != (Container *) NULL;
	    tsdPtr->firstContainerPtr = nextPtr) {
	nextPtr = tsdPtr->firstContainerPtr->nextPtr;
	ckfree((char *) tsdPtr->firstContainerPtr);
    }
    tsdPtr->firstContainerPtr = (Container *) NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * TkpTestembedCmd --
 *
 *	Test command for the embedding facility.
 *
 * Results:
 *	Always returns TCL_OK.
 *
 * Side effects:
 *	Currently it does not do anything.
 *
 *----------------------------------------------------------------------
 */

	/* ARGSUSED */
int
TkpTestembedCmd(
    ClientData clientData,
    Tcl_Interp *interp,
    int argc,
    CONST char **argv)
{
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_DetachEmbeddedWindow --
 *
 *	This function detaches an embedded window
 *
 * Results:
 *	No return value. Detach the embedded window.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static
void
Tk_DetachEmbeddedWindow(
    TkWindow *winPtr,		/* an embedded window */
    BOOL detachFlag)		/* a flag of truely detaching */
{
    TkpWinToplevelDetachWindow(winPtr);
    if(detachFlag) {
	TkpWinToplevelOverrideRedirect(winPtr, 0);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_MapEmbeddedWindow --
 *
 *	This function is required for mapping an embedded window during idle.
 *	The input winPtr must be preserved using Tcl_Preserve before call this
 *	function and will be released by this function.
 *
 * Results:
 *	No return value. Map the embedded window if it is not dead.
 *
 * Side effects:
 *	The embedded window may change its state as the container's.
 *
 *----------------------------------------------------------------------
 */

static
void Tk_MapEmbeddedWindow(
    TkWindow *winPtr)		/* Top-level window that's about to be
				 * mapped. */
{
    if(!(winPtr->flags & TK_ALREADY_DEAD)) {
	HWND hwnd = (HWND)winPtr->privatePtr;
	int state = SendMessage(hwnd, TK_STATE, -1, -1) - 1;

	if (state < 0 || state > 3) {
	    state = NormalState;
	}

	while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {
	    /* empty body */
	}

	TkpWmSetState(winPtr, state);
	TkWmMapWindow(winPtr);
    }
    Tcl_Release((ClientData)winPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkpUseWindow --
 *
 *	This procedure causes a Tk window to use a given Windows handle for a
 *	window as its underlying window, rather than a new Windows window
 *	being created automatically. It is invoked by an embedded application
 *	to specify the window in which the application is embedded.
 *
 *	This procedure uses a simple attachment protocol by sending TK_INFO
 *	messages to the window to use with two sub messages:
 *
 *	    TK_CONTAINER_VERIFY - if a window handles this message, it should
 *		return either a (long)hwnd for a container or a -(long)hwnd
 *		for a non-container.
 *
 *	    TK_CONTAINER_ISAVAILABLE - a container window should return either
 *		a TRUE (non-zero) if it is available for use or a FALSE (zero)
 *		othersize.
 *
 *	The TK_INFO messages are required in order to verify if the window to
 *	use is a valid container. Without an id verification, an invalid
 *	window attachment may cause unexpected crashes/panics (bug 1096074).
 *	Additional sub messages may be definded/used in future for other
 *	needs.
 *
 *	We do not enforce the above protocol for the reason of backward
 *	compatibility. If the window to use is unable to handle TK_INFO
 *	messages (e.g., legacy Tk container applications before 8.5), a dialog
 *	box with a warning message pops up and the user is asked to confirm if
 *	the attachment should proceed. However, we may have to enforce it in
 *	future.
 *
 * Results:
 *	The return value is normally TCL_OK. If an error occurred (such as if
 *	the argument does not identify a legal Windows window handle or it is
 *	already in use or a cancel button is pressed by a user in confirming
 *	the use window as a Tk container) the return value is TCL_ERROR and an
 *	error message is left in the the interp's result if interp is not
 *	NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkpUseWindow(
    Tcl_Interp *interp,		/* If not NULL, used for error reporting if
				 * string is bogus. */
    Tk_Window tkwin,		/* Tk window that does not yet have an
				 * associated X window. */
    CONST char *string)		/* String identifying an X window to use for
				 * tkwin; must be an integer value. */
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    int id;
    HWND hwnd;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

/*
    if (winPtr->window != None) {
	Tcl_AppendResult(interp, "can't modify container after widget is created", NULL);
	return TCL_ERROR;
    }
*/

    if (strcmp(string, "") == 0) {
	if (winPtr->flags & TK_EMBEDDED) {
	    Tk_DetachEmbeddedWindow(winPtr, TRUE);
	}
	return TCL_OK;
    }

    if (Tcl_GetInt(interp, string, &id) != TCL_OK) {
	return TCL_ERROR;
    }
    hwnd = (HWND) id;
    if ((HWND)winPtr->privatePtr == hwnd) {
	return TCL_OK;
    }

    /*
     * Check if the window is a valid handle. If it is invalid, return
     * TCL_ERROR and potentially leave an error message in the interp's
     * result.
     */

    if (!IsWindow(hwnd)) {
	if (interp != NULL) {
	    Tcl_AppendResult(interp, "window \"", string,
		    "\" doesn't exist", NULL);
	}
	return TCL_ERROR;
    }

    id = SendMessage(hwnd, TK_INFO, TK_CONTAINER_VERIFY, 0);
    if (id == (long)hwnd) {
	if (!SendMessage(hwnd, TK_INFO, TK_CONTAINER_ISAVAILABLE, 0)) {
    	    Tcl_AppendResult(interp, "The container is already in use", NULL);
	    return TCL_ERROR;
	}
    } else if (id == -(long)hwnd) {
	Tcl_AppendResult(interp, "the window to use is not a Tk container",
		NULL);
	return TCL_ERROR;
    } else {
	/*
	 * Proceed if the user decide to do so because it can be a legacy
	 * container application. However we may have to return a TCL_ERROR in
	 * order to avoid bug 1096074 in future.
	 */

	char msg[256];

	sprintf(msg, "Unable to get information of window \"%.80s\".  Attach to this\nwindow may have unpredictable results if it is not a valid container.\n\nPress Ok to proceed or Cancel to abort attaching.", string);
	if (IDCANCEL == MessageBox(hwnd, msg, "Tk Warning",
		MB_OKCANCEL | MB_ICONWARNING)) {
    	    Tcl_SetResult(interp, "Operation has been canceled", TCL_STATIC);
	    return TCL_ERROR;
	}
    }

    Tk_DetachEmbeddedWindow(winPtr, FALSE);

    /*
     * Store the parent window in the platform private data slot so
     * TkWmMapWindow can use it when creating the wrapper window.
     */

    winPtr->privatePtr = (struct TkWindowPrivate*) hwnd;
    winPtr->flags |= TK_EMBEDDED;
    winPtr->flags &= (~(TK_MAPPED));

    /*
     * Preserve the winPtr and create an idle handler to map the embedded
     * window.
     */

    Tcl_Preserve((ClientData)winPtr);
    Tcl_DoWhenIdle(Tk_MapEmbeddedWindow,(ClientData)winPtr);

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TkpMakeContainer --
 *
 *	This procedure is called to indicate that a particular window will be
 *	a container for an embedded application. This changes certain aspects
 *	of the window's behavior, such as whether it will receive events
 *	anymore.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TkpMakeContainer(
    Tk_Window tkwin)
{
    TkWindow *winPtr = (TkWindow *) tkwin;
    Container *containerPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /*
     * Register the window as a container so that, for example, we can find
     * out later if the embedded app. is in the same process.
     */

    Tk_MakeWindowExist(tkwin);
    containerPtr = (Container *) ckalloc(sizeof(Container));
    containerPtr->parentPtr = winPtr;
    containerPtr->parentHWnd = Tk_GetHWND(Tk_WindowId(tkwin));
    containerPtr->embeddedHWnd = NULL;
    containerPtr->embeddedPtr = NULL;
    containerPtr->embeddedMenuHWnd = NULL;
    containerPtr->nextPtr = tsdPtr->firstContainerPtr;
    tsdPtr->firstContainerPtr = containerPtr;
    winPtr->flags |= TK_CONTAINER;

    /*
     * Unlike in tkUnixEmbed.c, we don't make any requests for events in the
     * embedded window here. Now we just allow the embedding of another TK
     * application into TK windows. When the embedded window makes a request,
     * that will be done by sending to the container window a WM_USER message,
     * which will be intercepted by TkWinContainerProc.
     *
     * We need to get structure events of the container itself, though.
     */

    Tk_CreateEventHandler(tkwin, StructureNotifyMask,
	    ContainerEventProc, (ClientData) containerPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * EmbeddedEventProc --
 *
 *	This procedure is invoked by the Tk event dispatcher when various
 *	useful events are received for a window that is embedded in another
 *	application.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Our internal state gets cleaned up when an embedded window is
 *	destroyed.
 *
 *----------------------------------------------------------------------
 */

static void
EmbeddedEventProc(
    ClientData clientData,	/* Token for container window. */
    XEvent *eventPtr)		/* ResizeRequest event. */
{
    TkWindow *winPtr = (TkWindow *) clientData;

    if (eventPtr->type == DestroyNotify) {
	EmbedWindowDeleted(winPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkWinEmbeddedEventProc --
 *
 *	This procedure is invoked by the Tk event dispatcher when various
 *	useful events are received for the *children* of a container window.
 *	It forwards relevant information, such as geometry requests, from the
 *	events into the container's application.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on the event. For example, when ConfigureRequest events occur,
 *	geometry information gets set for the container window.
 *
 *----------------------------------------------------------------------
 */

LRESULT
TkWinEmbeddedEventProc(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    int result = 1;
    Container *containerPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /*
     * Find the Container structure associated with the parent window.
     */

    for (containerPtr = tsdPtr->firstContainerPtr;
	    containerPtr && containerPtr->parentHWnd != hwnd;
	    containerPtr = containerPtr->nextPtr) {
	/* empty loop body */
    }

    if (containerPtr) {
	TkWindow *topwinPtr = NULL;
	if(Tk_IsTopLevel(containerPtr->parentPtr)) {
	    topwinPtr = containerPtr->parentPtr;
	}
	switch (message) {
	case TK_INFO:
	    /*
	     * An embedded window may send this message for container
	     * verification and availability before attach.
	     *
	     * wParam - a sub message
	     *
	     *	    TK_CONTAINER_ISAVAILABLE - if the container is available
	     *		for use?
	     *		result = 1 for yes and 0 for no;
	     *
	     *	    TK_CONTAINER_VERIFY - request the container to verify its
	     *		identification
	     *		result =  (long)hwnd if this window is a container
	     *			 -(long)hwnd otherwise
	     *
	     * lParam - N/A
	     */

	    switch(wParam) {
	    case TK_CONTAINER_ISAVAILABLE:
		result = containerPtr->embeddedHWnd == NULL? 1:0;
		break;
	    case TK_CONTAINER_VERIFY:
		result = (long)containerPtr->parentHWnd;
		break;
	    default:
		result = 0;
	    }
	    break;

	case TK_ATTACHWINDOW:
	    /*
	     * An embedded window (either from this application or from
	     * another application) is trying to attach to this container. We
	     * attach it only if this container is not yet containing any
	     * window.
	     *
	     * wParam - a handle of an embedded window
	     * lParam - N/A
	     *
	     * An embedded window may send this message with a wParam of NULL
	     * to test if a window is able to provide embedding service. The
	     * container returns its window handle for accepting the
	     * attachment and identifying itself or a zero for being already
	     * in use.
	     *
	     * Return value:
	     * 0    - the container is unable to be used.
	     * hwnd - the container is ready to be used.
	     */
	    if (containerPtr->embeddedHWnd == NULL) {
		if (wParam) {
		    TkWindow *winPtr = (TkWindow *)
			    Tk_HWNDToWindow((HWND) wParam);
		    if (winPtr) {
			winPtr->flags |= TK_BOTH_HALVES;
			containerPtr->embeddedPtr = winPtr;
			containerPtr->parentPtr->flags |= TK_BOTH_HALVES;
		    }
		    containerPtr->embeddedHWnd = (HWND)wParam;
		}
		result = (long)containerPtr->parentHWnd;
	    } else {
		result = 0;
	    }
	    break;

	case TK_DETACHWINDOW:
	    /*
	     * An embedded window notifies the container that it is detached.
	     * The container should clearn the related variables and redraw
	     * its window.
	     *
	     * wParam - N/A
	     * lParam - N/A
	     *
	     * Return value:
	     * 0	- the message is not processed.
	     * others	- the message is processed.
	     */

	    containerPtr->embeddedMenuHWnd = NULL;
	    containerPtr->embeddedHWnd = NULL;
	    containerPtr->parentPtr->flags &= ~TK_BOTH_HALVES;
	    if (topwinPtr) {
		TkWinSetMenu((Tk_Window) topwinPtr, 0);
	    }
	    InvalidateRect(hwnd, NULL, TRUE);
	    break;

	case TK_GEOMETRYREQ:
	    /*
	     * An embedded window requests a window size change.
	     *
	     * wParam - window width
	     * lParam - window height
	     *
	     * Return value:
	     * 0	- the message is not processed.
	     * others	- the message is processed.
	     */

	    EmbedGeometryRequest(containerPtr, (int)wParam, lParam);
	    break;

	case TK_RAISEWINDOW:
	    /*
	     * An embedded window requests to change its Z-order.
	     *
	     * wParam - a window handle as a z-order stack reference
	     * lParam - a flag of above-below: 0 - above; 1 or others: - below
	     *
	     * Return value:
	     * 0	- the message is not processed.
	     * others	- the message is processed.
	     */

	    TkWinSetWindowPos(GetParent(containerPtr->parentHWnd),
		    (HWND)wParam, (int)lParam);
	    break;

	case TK_GETFRAMEWID:
	    /*
	     * An embedded window requests to get the frame window's id.
	     *
	     * wParam - N/A
	     * lParam - N/A
	     *
	     * Return vlaue:
	     *
	     * A handle of the frame window. If it is not availble, a zero is
	     * returned.
	     */
	    if (topwinPtr) {
		result = (long)GetParent(containerPtr->parentHWnd);
	    } else {
		topwinPtr = containerPtr->parentPtr;
		while (!(topwinPtr->flags & TK_TOP_HIERARCHY)) {
		    topwinPtr = topwinPtr->parentPtr;
		}
		if (topwinPtr && topwinPtr->window) {
		    result = (long)GetParent(Tk_GetHWND(topwinPtr->window));
		} else {
		    result = 0;
		}
	    }
	    break;

	case TK_CLAIMFOCUS:
	    /*
	     * An embedded window requests a focus.
	     *
	     * wParam - a flag of forcing focus
	     * lParam - N/A
	     *
	     * Return value:
	     * 0    - the message is not processed
	     * 1    - the message is processed
	     */

	    if (!SetFocus(containerPtr->embeddedHWnd) && wParam) {
		/*
		 * forcing focus TBD
		 */
	    }
	    break;

	case TK_WITHDRAW:
	    /*
	     * An embedded window requests withdraw.
	     *
	     * wParam	- N/A
	     * lParam	- N/A
	     *
	     * Return value
	     * 0    - the message is not processed
	     * 1    - the message is processed
	     */

	    if (topwinPtr) {
		TkpWinToplevelWithDraw(topwinPtr);
	    } else {
		result = 0;
	    }
	    break;

	case TK_ICONIFY:
	    /*
	     * An embedded window requests iconification.
	     *
	     * wParam	- N/A
	     * lParam	- N/A
	     *
	     * Return value
	     * 0    - the message is not processed
	     * 1    - the message is processed
	     */

	    if (topwinPtr) {
		TkpWinToplevelIconify(topwinPtr);
	    } else {
		result = 0;
	    }
	    break;

	case TK_DEICONIFY:
	    /*
	     * An embedded window requests deiconification.
	     *
	     * wParam	- N/A
	     * lParam	- N/A
	     *
	     * Return value
	     * 0    - the message is not processed
	     * 1    - the message is processed
	     */
	    if (topwinPtr) {
		TkpWinToplevelDeiconify(topwinPtr);
	    } else {
		result = 0;
	    }
	    break;

	case TK_MOVEWINDOW:
	    /*
	     * An embedded window requests to move position if both wParam and
	     * lParam are greater or equal to 0.
	     *	    wParam - x value of the frame's upper left
	     *	    lParam - y value of the frame's upper left
	     *
	     * Otherwise an embedded window requests the current position
	     *
	     * Return value: an encoded window position in a 32bit long, i.e,
	     * ((x << 16) & 0xffff0000) | (y & 0xffff)
	     *
	     * Only a toplevel container may move the embedded.
	     */

	    result = TkpWinToplevelMove(containerPtr->parentPtr,
		    wParam, lParam);
	    break;

	case TK_OVERRIDEREDIRECT:
	    /*
	     * An embedded window request overrideredirect.
	     *
	     * wParam
	     *	0	- add a frame if there is no one
	     *  1	- remove the frame if there is a one
	     *  < 0	- query the current overrideredirect value
	     *
	     * lParam	- N/A
	     *
	     * Return value:
	     * 1 + the current value of overrideredirect if the container is a
	     * toplevel. Otherwise 0.
	     */
	    if (topwinPtr) {
		result = 1 + TkpWinToplevelOverrideRedirect(topwinPtr, wParam);
	    } else {
		result = 0;
	    }
	    break;

	case TK_SETMENU:
	    /*
	     * An embedded requests to set a menu.
	     *
	     * wParam	- a menu handle
	     * lParam	- a menu window handle
	     *
	     * Return value:
	     * 1    - the message is processed
	     * 0    - the message is not processed
	     */
	    if (topwinPtr) {
		containerPtr->embeddedMenuHWnd = (HWND)lParam;
		TkWinSetMenu((Tk_Window)topwinPtr, (HMENU)wParam);
	    } else {
		result = 0;
	    }
	    break;

	case TK_STATE:
	    /*
	     * An embedded window request set/get state services.
	     *
	     * wParam	- service directive
	     *	    0 - 3 for setting state
	     *		0 - withdrawn state
	     *		1 - normal state
	     *		2 - zoom state
	     *		3 - icon state
	     * others for gettting state
	     *
	     * lParam	- N/A
	     *
	     * Return value
	     * 1 + the current state or 0 if the container is not a toplevel
	     */

	    if (topwinPtr) {
		if (wParam >= 0 && wParam <= 3) {
		    TkpWmSetState(topwinPtr, wParam);
		}
		result = 1+TkpWmGetState(topwinPtr);
	    } else {
		result = 0;
	    }
	    break;

	    /*
	     * Return 0 since the current Tk container implementation is
	     * unable to provide following services.
	     */
	default:
	    result = 0;
	    break;
	}
    } else {
	if ((message == TK_INFO) && (wParam == TK_CONTAINER_VERIFY)) {
	    /*
	     * Reply the message sender: this is not a Tk container
	     */

	    return -(long)hwnd;
	} else {
	    result = 0;
	}
    }

    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * EmbedGeometryRequest --
 *
 *	This procedure is invoked when an embedded application requests a
 *	particular size. It processes the request (which may or may not
 *	actually resize the window) and reflects the results back to the
 *	embedded application.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	If we deny the child's size change request, a Configure event is
 *	synthesized to let the child know that the size is the same as it used
 *	to be. Events get processed while we're waiting for the geometry
 *	managers to do their thing.
 *
 *----------------------------------------------------------------------
 */

void
EmbedGeometryRequest(
    Container *containerPtr,	/* Information about the container window. */
    int width, int height)	/* Size that the child has requested. */
{
    TkWindow *winPtr = containerPtr->parentPtr;

    /*
     * Forward the requested size into our geometry management hierarchy via
     * the container window. We need to send a Configure event back to the
     * embedded application even if we decide not to resize the window; to
     * make this happen, process all idle event handlers synchronously here
     * (so that the geometry managers have had a chance to do whatever they
     * want to do), and if the window's size didn't change then generate a
     * configure event.
     */

    Tk_GeometryRequest((Tk_Window)winPtr, width, height);

    if (containerPtr->embeddedHWnd != NULL) {
	while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {
	    /* Empty loop body. */
	}

	SetWindowPos(containerPtr->embeddedHWnd, NULL, 0, 0,
		winPtr->changes.width, winPtr->changes.height, SWP_NOZORDER);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * ContainerEventProc --
 *
 *	This procedure is invoked by the Tk event dispatcher when various
 *	useful events are received for the container window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Depends on the event. For example, when ConfigureRequest events occur,
 *	geometry information gets set for the container window.
 *
 *----------------------------------------------------------------------
 */

static void
ContainerEventProc(
    ClientData clientData,	/* Token for container window. */
    XEvent *eventPtr)		/* ResizeRequest event. */
{
    Container *containerPtr = (Container *)clientData;
    Tk_Window tkwin = (Tk_Window)containerPtr->parentPtr;

    if (eventPtr->type == ConfigureNotify) {
	/*
	 * Resize the embedded window, if there is any.
	 */

	if (containerPtr->embeddedHWnd) {
	    SetWindowPos(containerPtr->embeddedHWnd, NULL, 0, 0,
		    Tk_Width(tkwin), Tk_Height(tkwin), SWP_NOZORDER);
	}
    } else if (eventPtr->type == DestroyNotify) {
	/*
	 * The container is gone, remove it from the list.
	 */

	EmbedWindowDeleted(containerPtr->parentPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkpGetOtherWindow --
 *
 *	If both the container and embedded window are in the same process,
 *	this procedure will return either one, given the other.
 *
 * Results:
 *	If winPtr is a container, the return value is the token for the
 *	embedded window, and vice versa. If the "other" window isn't in this
 *	process, NULL is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

TkWindow *
TkpGetOtherWindow(
    TkWindow *winPtr)		/* Tk's structure for a container or embedded
				 * window. */
{
    Container *containerPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    for (containerPtr = tsdPtr->firstContainerPtr; containerPtr != NULL;
	    containerPtr = containerPtr->nextPtr) {
	if (containerPtr->embeddedPtr == winPtr) {
	    return containerPtr->parentPtr;
	} else if (containerPtr->parentPtr == winPtr) {
	    return containerPtr->embeddedPtr;
	}
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetEmbeddedHWnd --
 *
 *	This function returns the embedded window id.
 *
 * Results:
 *	If winPtr is a container, the return value is the HWND for the
 *	embedded window. Otherwise it returns NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

HWND
Tk_GetEmbeddedHWnd(
    TkWindow *winPtr)
{
    Container *containerPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    for (containerPtr = tsdPtr->firstContainerPtr; containerPtr != NULL;
	    containerPtr = containerPtr->nextPtr) {
	if (containerPtr->parentPtr == winPtr) {
	    return containerPtr->embeddedHWnd;
	}
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * Tk_GetEmbeddedMenuHWND --
 *
 *	This function returns the embedded menu window id.
 *
 * Results:
 *	If winPtr is a container, the return value is the HWND for the
 *	embedded menu window. Otherwise it returns NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

HWND
Tk_GetEmbeddedMenuHWND(
    Tk_Window tkwin)
{
    TkWindow *winPtr = (TkWindow*)tkwin;
    Container *containerPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    for (containerPtr = tsdPtr->firstContainerPtr; containerPtr != NULL;
	    containerPtr = containerPtr->nextPtr) {
	if (containerPtr->parentPtr == winPtr) {
	    return containerPtr->embeddedMenuHWnd;
	}
    }
    return NULL;
}

/*
 *----------------------------------------------------------------------
 *
 * TkpClaimFocus --
 *
 *	This procedure is invoked when someone asks or the input focus to be
 *	put on a window in an embedded application, but the application
 *	doesn't currently have the focus. It requests the input focus from the
 *	container application.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The input focus may change.
 *
 *----------------------------------------------------------------------
 */

void
TkpClaimFocus(
    TkWindow *topLevelPtr,	/* Top-level window containing desired focus
				 * window; should be embedded. */
    int force)			/* One means that the container should claim
				 * the focus if it doesn't currently have
				 * it. */
{
    HWND hwnd = GetParent(Tk_GetHWND(topLevelPtr->window));
    SendMessage(hwnd, TK_CLAIMFOCUS, (WPARAM) force, 0);
}

/*
 *----------------------------------------------------------------------
 *
 * TkpRedirectKeyEvent --
 *
 *	This procedure is invoked when a key press or release event arrives
 *	for an application that does not believe it owns the input focus.
 *	This can happen because of embedding; for example, X can send an event
 *	to an embedded application when the real focus window is in the
 *	container application and is an ancestor of the container. This
 *	procedure's job is to forward the event back to the application where
 *	it really belongs.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The event may get sent to a different application.
 *
 *----------------------------------------------------------------------
 */

void
TkpRedirectKeyEvent(
    TkWindow *winPtr,		/* Window to which the event was originally
				 * reported. */
    XEvent *eventPtr)		/* X event to redirect (should be KeyPress or
				 * KeyRelease). */
{
    /* not implemented */
}

/*
 *----------------------------------------------------------------------
 *
 * EmbedWindowDeleted --
 *
 *	This procedure is invoked when a window involved in embedding (as
 *	either the container or the embedded application) is destroyed. It
 *	cleans up the Container structure for the window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	A Container structure may be freed.
 *
 *----------------------------------------------------------------------
 */

static void
EmbedWindowDeleted(
    TkWindow *winPtr)		/* Tk's information about window that was
				 * deleted. */
{
    Container *containerPtr, *prevPtr;
    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));

    /*
     * Find the Container structure for this window work. Delete the
     * information about the embedded application and free the container's
     * record. The main container may be null. [Bug #476176]
     */

    prevPtr = NULL;
    containerPtr = tsdPtr->firstContainerPtr;
    if (containerPtr == NULL) return;
    while (1) {
	if (containerPtr->embeddedPtr == winPtr) {
	    containerPtr->embeddedHWnd = NULL;
	    containerPtr->embeddedPtr = NULL;
	    break;
	}
	if (containerPtr->parentPtr == winPtr) {
	    SendMessage(containerPtr->embeddedHWnd, WM_CLOSE, 0, 0);
	    containerPtr->parentPtr = NULL;
	    containerPtr->embeddedPtr = NULL;
	    break;
	}
	prevPtr = containerPtr;
	containerPtr = containerPtr->nextPtr;
	if (containerPtr == NULL) {
	    return;
	}
    }
    if ((containerPtr->embeddedPtr == NULL)
	    && (containerPtr->parentPtr == NULL)) {
	if (prevPtr == NULL) {
	    tsdPtr->firstContainerPtr = containerPtr->nextPtr;
	} else {
	    prevPtr->nextPtr = containerPtr->nextPtr;
	}
	ckfree((char *) containerPtr);
    }
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
